darwin-agentic-cloud 3.0.1__tar.gz → 3.0.3__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 (93) hide show
  1. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/.gitignore +7 -0
  2. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/CHANGELOG.md +36 -0
  3. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/PKG-INFO +7 -2
  4. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/README.md +6 -1
  5. darwin_agentic_cloud-3.0.3/att.json +51 -0
  6. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/__init__.py +1 -1
  7. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/cli.py +59 -0
  8. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/ui.py +97 -0
  9. darwin_agentic_cloud-3.0.3/hello.py +1 -0
  10. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/pyproject.toml +1 -1
  11. darwin_agentic_cloud-3.0.3/tests/cli/test_run.py +314 -0
  12. darwin_agentic_cloud-3.0.3/tests/runtime/__init__.py +0 -0
  13. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/.dockerignore +0 -0
  14. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/CODE_OF_CONDUCT.md +0 -0
  15. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/CONTRIBUTING.md +0 -0
  16. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/DARWIN_AGENTIC_CLOUD_PHASE_2_PROGRESS_2of3.md +0 -0
  17. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/DARWIN_AGENTIC_CLOUD_PHASE_2_SPEC.md +0 -0
  18. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/Dockerfile +0 -0
  19. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/LAUNCH_POST_v3.0.0.md +0 -0
  20. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/LICENSE +0 -0
  21. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/Makefile +0 -0
  22. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/QUICKSTART.md +0 -0
  23. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/SECURITY.md +0 -0
  24. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/__init__.py +0 -0
  25. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/admin_cli.py +0 -0
  26. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/attestation.py +0 -0
  27. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/class_keys.py +0 -0
  28. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/cost.py +0 -0
  29. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/hashing.py +0 -0
  30. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/mcp_server.py +0 -0
  31. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/router.py +0 -0
  32. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/runtime.py +0 -0
  33. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/runtime_v02.py +0 -0
  34. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/sandbox.py +0 -0
  35. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/schemas/__init__.py +0 -0
  36. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/schemas/attestation_v0_2.json +0 -0
  37. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/server.py +0 -0
  38. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/signing.py +0 -0
  39. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/storage.py +0 -0
  40. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/__init__.py +0 -0
  41. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/aws_batch.py +0 -0
  42. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/aws_batch_event.py +0 -0
  43. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/aws_lambda.py +0 -0
  44. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/aws_lambda_event.py +0 -0
  45. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/aws_pricing.py +0 -0
  46. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/base.py +0 -0
  47. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/identity.py +0 -0
  48. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/local_docker.py +0 -0
  49. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/substrate/modal.py +0 -0
  50. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/templates/demo.html +0 -0
  51. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/templates/demo_attestation.json +0 -0
  52. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/templates/docs.html +0 -0
  53. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/darwin/agenticcloud/types.py +0 -0
  54. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/docker-entrypoint.sh +0 -0
  55. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/docs/AGENT_INVENTORY.md +0 -0
  56. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/examples/example_workloads/hello.py +0 -0
  57. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/examples/hello.py +0 -0
  58. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/fly.toml +0 -0
  59. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/__init__.py +0 -0
  60. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/Dockerfile.batch-runner-python +0 -0
  61. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/Dockerfile.runner-node +0 -0
  62. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/Dockerfile.runner-python +0 -0
  63. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/__init__.py +0 -0
  64. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/batch_deploy.py +0 -0
  65. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/batch_runner.py +0 -0
  66. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/deploy.py +0 -0
  67. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/runner.mjs +0 -0
  68. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/aws_runner/runner.py +0 -0
  69. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/bake_demo_attestation.py +0 -0
  70. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/batch_smoketest.py +0 -0
  71. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/infra/modal_smoketest.py +0 -0
  72. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/__init__.py +0 -0
  73. {darwin_agentic_cloud-3.0.1/tests/runtime → darwin_agentic_cloud-3.0.3/tests/cli}/__init__.py +0 -0
  74. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/fixtures/ec2_ondemand_m5xlarge.json +0 -0
  75. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/fixtures/ec2_spot_history_m5xlarge.json +0 -0
  76. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/runtime/test_router.py +0 -0
  77. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/runtime/test_runtime_v02.py +0 -0
  78. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_aws_batch.py +0 -0
  79. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_aws_lambda.py +0 -0
  80. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_aws_pricing.py +0 -0
  81. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_base.py +0 -0
  82. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_identity.py +0 -0
  83. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_local_docker.py +0 -0
  84. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/substrate/test_modal.py +0 -0
  85. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_admin_cli.py +0 -0
  86. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_attestation.py +0 -0
  87. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_class_keys.py +0 -0
  88. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_cost.py +0 -0
  89. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_server_signing.py +0 -0
  90. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_smoke.py +0 -0
  91. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_storage.py +0 -0
  92. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/tests/test_ui.py +0 -0
  93. {darwin_agentic_cloud-3.0.1 → darwin_agentic_cloud-3.0.3}/uv.lock +0 -0
@@ -100,3 +100,10 @@ logs/
100
100
  # DAC-specific
101
101
  attestations/
102
102
  .dac-state/
103
+
104
+ # macOS Finder duplicate artifacts
105
+ * 2.md
106
+ * 2.sh
107
+ * 2.lock
108
+ *\ 2/
109
+ README.v2.0.0.md.bak
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.0.3] — 2026-05-28
11
+
12
+ ### Added
13
+
14
+ - CLI integration tests for `darwin run` (`tests/cli/test_run.py`). 15 new tests
15
+ covering argument parsing (no-args error, help output, documented flags),
16
+ workload resolution (file path vs inline code, nonexistent-file fallback),
17
+ flag handling (--substrate, --cost-cap, --timeout, --memory, --language),
18
+ output formats (--save writes valid JSON, --json emits parseable output), and
19
+ smoke regression for the file-exists-and-inline-code happy paths. Tests use
20
+ `typer.testing.CliRunner` with the underlying `darwin.run()` mocked so they
21
+ run without Docker or AWS credentials.
22
+ - New `tests/cli/` directory + `__init__.py` for future per-verb CLI test files.
23
+
24
+ ### Internal
25
+
26
+ - Test suite total: 388 → 403 passing.
27
+
28
+ ## [3.0.2] — 2026-05-28
29
+
30
+ ### Added
31
+
32
+ - First-run welcome banner. When a user runs any `darwin` command for the first
33
+ time after install, an ASCII brand banner prints with the tagline, motto, three
34
+ starter commands, and links to demo, repo, and docs. Shown once per install
35
+ via a marker file at `~/.darwin/welcomed`. Suppressed in non-interactive shells
36
+ (pipes, scripts, CI) and when `DARWIN_SUPPRESS_WELCOME=1` is set.
37
+ - `darwin welcome` command to re-trigger the banner on demand.
38
+
39
+ ### Internal
40
+
41
+ - New `render_welcome()` and `print_welcome()` functions in
42
+ `darwin/agenticcloud/ui.py`, reusing the existing `BANNER`, `BRAND_GREEN`,
43
+ `BRAND_AMBER`, and `BRAND_DIM` brand tokens for visual continuity with the
44
+ attestation cert panel.
45
+
10
46
  ## [3.0.1] — 2026-05-27
11
47
 
12
48
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: darwin-agentic-cloud
3
- Version: 3.0.1
3
+ Version: 3.0.3
4
4
  Summary: Darwin — adaptive software systems. Includes darwin.agenticcloud, the verifiable agentic cloud. Open-source compute for AI agents with cryptographically signed attestations.
5
5
  Project-URL: Homepage, https://github.com/vje013/darwin-agentic-cloud
6
6
  Project-URL: Repository, https://github.com/vje013/darwin-agentic-cloud
@@ -275,7 +275,7 @@ Description-Content-Type: text/markdown
275
275
  [![Python: 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
276
276
  [![Schema: v0.2](https://img.shields.io/badge/schema-v0.2-FFB86C.svg)](https://darwin-agentic-cloud.fly.dev/.well-known/schemas/attestation/v0.2)
277
277
 
278
- > **Verifiable compute for AI agents.** Every workload signed. Every cost capped. Every routing decision auditable. Open verification, paid signing.
278
+ > **Verifiable compute for AI agents.** Every workload signed. Every cost capped. Every routing decision auditable. Open source, verified, free compute.
279
279
 
280
280
  **Install:** `pip install darwin-agentic-cloud`
281
281
 
@@ -285,6 +285,11 @@ Description-Content-Type: text/markdown
285
285
 
286
286
  **v0.2 Schema:** https://darwin-agentic-cloud.fly.dev/.well-known/schemas/attestation/v0.2
287
287
 
288
+ ---
289
+ ## Darwin's Mission
290
+
291
+ Increase AI adoption by increasing AI safety.
292
+
288
293
  ---
289
294
 
290
295
  ## The question agents can't answer today
@@ -6,7 +6,7 @@
6
6
  [![Python: 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
7
7
  [![Schema: v0.2](https://img.shields.io/badge/schema-v0.2-FFB86C.svg)](https://darwin-agentic-cloud.fly.dev/.well-known/schemas/attestation/v0.2)
8
8
 
9
- > **Verifiable compute for AI agents.** Every workload signed. Every cost capped. Every routing decision auditable. Open verification, paid signing.
9
+ > **Verifiable compute for AI agents.** Every workload signed. Every cost capped. Every routing decision auditable. Open source, verified, free compute.
10
10
 
11
11
  **Install:** `pip install darwin-agentic-cloud`
12
12
 
@@ -16,6 +16,11 @@
16
16
 
17
17
  **v0.2 Schema:** https://darwin-agentic-cloud.fly.dev/.well-known/schemas/attestation/v0.2
18
18
 
19
+ ---
20
+ ## Darwin's Mission
21
+
22
+ Increase AI adoption by increasing AI safety.
23
+
19
24
  ---
20
25
 
21
26
  ## The question agents can't answer today
@@ -0,0 +1,51 @@
1
+ {
2
+ "attestation_id": "att_1a83a636569e4e93",
3
+ "schema": "darwin.cloud/agenticcloud/attestation/v0.2",
4
+ "issued_at": "2026-05-29T01:10:03Z",
5
+ "workload_spec_hash": "4fa10188f2faa0d07f25cb38df5492424b7f154d874e02463216ea827ae1748a",
6
+ "execution_result": {
7
+ "output_hash": "67ecdb031db5e6529d6edd8d25eb20dd443162fd8b3fdf4b121dbfc82e7906be",
8
+ "substrate": {
9
+ "id": "local-docker-v0",
10
+ "version": "0.1.0",
11
+ "identity_signature": "c/zmytDvWNdHsirKXs6LYyuT0CyEEhuStDb+CbzxVfsIzU/lgCVGphYNYzE/s0/GKxoRthL4PHfbnJGgoe2XAQ==",
12
+ "identity_signer_type": "darwin-class-key",
13
+ "identity_signer_key_id": "dac-class-local-docker-v0-ca698355dcb631e3",
14
+ "evidence_schema_id": "darwin.cloud/evidence/local-docker/v1",
15
+ "evidence": {
16
+ "container_status": "ok",
17
+ "exit_code": 0,
18
+ "stdout_hash": "67ecdb031db5e6529d6edd8d25eb20dd443162fd8b3fdf4b121dbfc82e7906be",
19
+ "stderr_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
20
+ "wall_time_sec": 0.11720395088195801
21
+ },
22
+ "extensions": {},
23
+ "tee_required": false
24
+ },
25
+ "cost_usd": 1.172e-05,
26
+ "stdout": "hello from a real attestation\n",
27
+ "stderr": ""
28
+ },
29
+ "value_added_service": {
30
+ "identity_signing": {
31
+ "schema_compliant": true,
32
+ "keylist_url": "https://darwin-agentic-cloud.fly.dev/.well-known/substrate-keys.json"
33
+ },
34
+ "cost_cap_enforcement": {
35
+ "cap_usd": 0.1,
36
+ "estimated_usd_max": 0.003,
37
+ "actual_usd": 1.172e-05,
38
+ "within_cap": true,
39
+ "headroom_usd": 0.09998828
40
+ },
41
+ "routing_decision": {
42
+ "policy": "pick_by_cost",
43
+ "chosen_substrate_id": "local-docker-v0",
44
+ "chosen_reason": "lowest_estimated_cost ($0.003000)",
45
+ "candidates_considered": 1,
46
+ "rejected_substrates": []
47
+ }
48
+ },
49
+ "signer_key_id": "dac-local-d1bf7cad25875cee",
50
+ "signature": "cyIS5v4zUkyIAyfISoBAYBZifIRQdAYX2ZU1o8KOSf/NGg/hC80lRqblFNCXRouETqeXL8A35UqJVx+2C8w+Bg=="
51
+ }
@@ -5,7 +5,7 @@ A family of modules:
5
5
  (more to come)
6
6
  """
7
7
 
8
- __version__ = "3.0.1"
8
+ __version__ = "3.0.3"
9
9
  __author__ = "Vladimir J Edouard"
10
10
  __license__ = "Apache-2.0"
11
11
 
@@ -14,6 +14,8 @@ Examples:
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
+ import os
18
+ import sys
17
19
  from datetime import UTC
18
20
  from pathlib import Path
19
21
  from typing import Annotated
@@ -35,6 +37,63 @@ app = typer.Typer(
35
37
  no_args_is_help=True,
36
38
  add_completion=False,
37
39
  )
40
+
41
+
42
+ # -------------------------------------------------------------------
43
+ # First-run welcome
44
+ # -------------------------------------------------------------------
45
+
46
+ _WELCOME_MARKER = Path.home() / ".darwin" / "welcomed"
47
+
48
+
49
+ def _show_welcome_if_first_run() -> None:
50
+ """Print the welcome banner exactly once per user install.
51
+
52
+ Suppressed in non-interactive shells, when piped, or when the user
53
+ sets DARWIN_SUPPRESS_WELCOME=1. The marker file at
54
+ ~/.darwin/welcomed records that the user has been welcomed.
55
+ """
56
+ try:
57
+ if not sys.stdout.isatty():
58
+ return
59
+ if os.environ.get("DARWIN_SUPPRESS_WELCOME"):
60
+ return
61
+ if _WELCOME_MARKER.exists():
62
+ return
63
+
64
+ from darwin import __version__ as _v
65
+ from darwin.agenticcloud.ui import print_welcome
66
+
67
+ print_welcome(Console(), version=_v)
68
+
69
+ _WELCOME_MARKER.parent.mkdir(parents=True, exist_ok=True)
70
+ _WELCOME_MARKER.touch()
71
+ except Exception:
72
+ # Welcome banner must never block real commands. If anything
73
+ # goes wrong here (terminal weirdness, IO error, etc.), swallow
74
+ # silently and proceed with the user's command.
75
+ pass
76
+
77
+
78
+ @app.callback()
79
+ def _root(ctx: typer.Context) -> None:
80
+ """Darwin Agentic Cloud — verifiable compute for AI agents."""
81
+ # The `welcome` command renders the banner itself; skip the
82
+ # auto-trigger here to avoid double-rendering.
83
+ if ctx.invoked_subcommand == "welcome":
84
+ return
85
+ _show_welcome_if_first_run()
86
+
87
+
88
+ @app.command()
89
+ def welcome() -> None:
90
+ """Show the Darwin welcome banner."""
91
+ from darwin import __version__ as _v
92
+ from darwin.agenticcloud.ui import print_welcome
93
+
94
+ print_welcome(Console(), version=_v)
95
+
96
+
38
97
  keys_app = typer.Typer(help="Manage signing keys.", no_args_is_help=True)
39
98
  attest_app = typer.Typer(help="Work with attestations.", no_args_is_help=True)
40
99
  history_app = typer.Typer(help="Query attestation history.", no_args_is_help=True)
@@ -91,6 +91,103 @@ def print_banner(
91
91
  console.print()
92
92
 
93
93
 
94
+ # -------------------------------------------------------------------
95
+ # First-run welcome
96
+ # -------------------------------------------------------------------
97
+
98
+
99
+ def render_welcome(
100
+ version: str,
101
+ demo_url: str = "https://darwin-agentic-cloud.fly.dev/demo",
102
+ repo_url: str = "https://github.com/vje013/darwin-agentic-cloud",
103
+ docs_url: str = "https://darwin-agentic-cloud.fly.dev/docs",
104
+ ) -> Group:
105
+ """First-run welcome: ASCII banner + tagline + 3 commands + links.
106
+
107
+ Visually distinct from `render_banner()` (which prints version/signer
108
+ metadata for repeat users). Designed to orient a new user in their
109
+ first 30 seconds with the tool.
110
+ """
111
+ parts: list = []
112
+
113
+ parts.append(Text(BANNER, style=f"bold {BRAND_GREEN}"))
114
+ parts.append(Text(""))
115
+
116
+ motto = Text()
117
+ motto.append(" ")
118
+ motto.append("✦", style=f"bold {BRAND_AMBER}")
119
+ motto.append(" SECURITAS · STABILITAS · SIGNUM ", style=BRAND_GREEN)
120
+ motto.append("✦", style=f"bold {BRAND_AMBER}")
121
+ parts.append(motto)
122
+ parts.append(Text(""))
123
+
124
+ parts.append(Text(" Verifiable and free compute for AI agents.", style=f"bold {BRAND_GREEN}"))
125
+ parts.append(
126
+ Text(" Cryptographically signed execution attestations.", style=f"bold {BRAND_GREEN}")
127
+ )
128
+ parts.append(Text(" MCP-native.", style=f"bold {BRAND_GREEN}"))
129
+ parts.append(Text(""))
130
+
131
+ parts.append(Text(" " + "─" * 59, style=BRAND_DIM))
132
+ parts.append(Text(""))
133
+
134
+ parts.append(Text(" Try it in 3 commands:", style=f"bold {BRAND_GREEN}"))
135
+ parts.append(Text(""))
136
+
137
+ cmd1 = Text(" 1. ", style=f"bold {BRAND_GREEN}")
138
+ cmd1.append("darwin list", style=f"bold {BRAND_GREEN}")
139
+ parts.append(cmd1)
140
+ parts.append(
141
+ Text(" See which compute substrates are available right now.", style=BRAND_GREEN)
142
+ )
143
+ parts.append(Text(""))
144
+
145
+ cmd2 = Text(" 2. ", style=f"bold {BRAND_GREEN}")
146
+ cmd2.append("echo 'print(\"hello\")' > hello.py", style=BRAND_GREEN)
147
+ parts.append(cmd2)
148
+ parts.append(Text(" darwin run hello.py --save att.json", style=f"bold {BRAND_GREEN}"))
149
+ parts.append(Text(" Execute a workload, get a signed attestation.", style=BRAND_GREEN))
150
+ parts.append(Text(""))
151
+
152
+ cmd3 = Text(" 3. ", style=f"bold {BRAND_GREEN}")
153
+ cmd3.append("darwin verify att.json", style=f"bold {BRAND_GREEN}")
154
+ parts.append(cmd3)
155
+ parts.append(
156
+ Text(" Cryptographically verify the attestation against", style=BRAND_GREEN)
157
+ )
158
+ parts.append(Text(" the public keylist.", style=BRAND_GREEN))
159
+ parts.append(Text(""))
160
+
161
+ parts.append(Text(" " + "─" * 59, style=BRAND_DIM))
162
+ parts.append(Text(""))
163
+
164
+ link_demo = Text(" Live demo: ", style=BRAND_GREEN)
165
+ link_demo.append(demo_url, style=f"underline {BRAND_GREEN}")
166
+ parts.append(link_demo)
167
+
168
+ link_repo = Text(" GitHub: ", style=BRAND_GREEN)
169
+ link_repo.append(repo_url, style=f"underline {BRAND_GREEN}")
170
+ parts.append(link_repo)
171
+
172
+ link_docs = Text(" Docs: ", style=BRAND_GREEN)
173
+ link_docs.append(docs_url, style=f"underline {BRAND_GREEN}")
174
+ parts.append(link_docs)
175
+
176
+ parts.append(Text(""))
177
+
178
+ parts.append(Text(" This message shown once per install.", style=BRAND_DIM))
179
+ parts.append(Text(" Run `darwin welcome` to see it again.", style=BRAND_DIM))
180
+
181
+ return Group(*parts)
182
+
183
+
184
+ def print_welcome(console: Console, version: str) -> None:
185
+ """Convenience: print the welcome with appropriate vertical spacing."""
186
+ console.print()
187
+ console.print(render_welcome(version=version))
188
+ console.print()
189
+
190
+
94
191
  # -------------------------------------------------------------------
95
192
  # Matrix boot sequence
96
193
  # -------------------------------------------------------------------
@@ -0,0 +1 @@
1
+ print("hello from a real attestation")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "darwin-agentic-cloud"
3
- version = "3.0.1"
3
+ version = "3.0.3"
4
4
  description = "Darwin — adaptive software systems. Includes darwin.agenticcloud, the verifiable agentic cloud. Open-source compute for AI agents with cryptographically signed attestations."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -0,0 +1,314 @@
1
+ """CLI integration tests for `darwin run`.
2
+
3
+ These tests exercise the Typer wrapper around `darwin.run()`. The
4
+ underlying `Runtime` is mocked so tests don't require a working Docker
5
+ daemon or AWS credentials. The point is to validate the CLI surface:
6
+ argument parsing, file resolution, flag handling, output formatting,
7
+ exit codes, and error messages.
8
+
9
+ See tests/runtime/test_runtime_v02.py for the underlying Runtime API
10
+ tests.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import re
17
+ from pathlib import Path
18
+ from unittest.mock import patch
19
+
20
+ import pytest
21
+ from typer.testing import CliRunner
22
+
23
+ from darwin.agenticcloud.cli import app
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Fixtures
27
+ # ---------------------------------------------------------------------------
28
+
29
+
30
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]")
31
+
32
+
33
+ def _strip_ansi(s):
34
+ return _ANSI_RE.sub("", s)
35
+
36
+
37
+ @pytest.fixture
38
+ def runner() -> CliRunner:
39
+ """Typer CLI runner that captures stdout and stderr."""
40
+ return CliRunner()
41
+
42
+
43
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]")
44
+
45
+
46
+ def _strip_ansi(s):
47
+ return _ANSI_RE.sub("", s)
48
+
49
+
50
+ @pytest.fixture
51
+ def fake_attestation() -> dict:
52
+ """A minimal v0.2-shaped attestation that the CLI can render."""
53
+ return {
54
+ "schema": "darwin.cloud/agenticcloud/attestation/v0.2",
55
+ "attestation_id": "test-attestation-id-0001",
56
+ "issued_at": "2026-05-29T01:00:00Z",
57
+ "workload_spec_hash": "sha256:" + "a" * 64,
58
+ "execution_result": {
59
+ "status": "ok",
60
+ "stdout": "hello\n",
61
+ "output_hash": "sha256:" + "b" * 64,
62
+ "cost_usd": 0.000012,
63
+ "substrate": {
64
+ "id": "local-docker-v0",
65
+ "version": "0.1.0",
66
+ "evidence_schema_id": "darwin.cloud/evidence/local-docker/v1",
67
+ "identity_signer_type": "darwin-class-key",
68
+ "identity_signer_key_id": "dac-class-local-docker-v0-test",
69
+ "identity_signature": "test-signature-b64",
70
+ "evidence": {
71
+ "container_status": "ok",
72
+ "exit_code": 0,
73
+ "wall_time_sec": 0.1,
74
+ "stdout_hash": "sha256:" + "b" * 64,
75
+ "stderr_hash": "sha256:" + "e" * 64,
76
+ },
77
+ },
78
+ "preflight_estimate": {"cost_usd_max": 0.0001},
79
+ },
80
+ "value_added_service": {
81
+ "identity_signing": {
82
+ "schema_compliant": True,
83
+ "keylist_url": "https://example/keys.json",
84
+ },
85
+ "cost_cap_enforcement": {
86
+ "cap_usd": 0.10,
87
+ "estimated_usd_max": 0.0001,
88
+ "actual_usd": 0.000012,
89
+ "within_cap": True,
90
+ "headroom_usd": 0.099988,
91
+ },
92
+ "routing_decision": {
93
+ "policy": "pick_by_cost",
94
+ "chosen_substrate_id": "local-docker-v0",
95
+ "chosen_reason": "lowest_estimated_cost ($0.0001)",
96
+ "candidates_considered": 1,
97
+ "rejected_substrates": [],
98
+ },
99
+ },
100
+ "signer_key_id": "dac-local-test-operator",
101
+ "signature": "test-outer-signature-b64",
102
+ }
103
+
104
+
105
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]")
106
+
107
+
108
+ def _strip_ansi(s):
109
+ return _ANSI_RE.sub("", s)
110
+
111
+
112
+ @pytest.fixture
113
+ def mocked_run(fake_attestation):
114
+ """Patch the underlying `darwin.run` to return our fake attestation.
115
+
116
+ Returns the patch object so individual tests can inspect call args
117
+ or override the return value if they need to test failure paths.
118
+ """
119
+ with patch("darwin.run") as mock:
120
+ mock.return_value = fake_attestation
121
+ yield mock
122
+
123
+
124
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]")
125
+
126
+
127
+ def _strip_ansi(s):
128
+ return _ANSI_RE.sub("", s)
129
+
130
+
131
+ @pytest.fixture
132
+ def hello_py(tmp_path: Path) -> Path:
133
+ """A real `hello.py` file in a tmp directory."""
134
+ p = tmp_path / "hello.py"
135
+ p.write_text('print("hello")\n')
136
+ return p
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Argument parsing
141
+ # ---------------------------------------------------------------------------
142
+
143
+
144
+ class TestArgumentParsing:
145
+ """The CLI surface: argument and flag handling."""
146
+
147
+ def test_run_with_no_args_exits_nonzero(self, runner: CliRunner):
148
+ """`darwin run` with no workload arg should error cleanly."""
149
+ result = runner.invoke(app, ["run"])
150
+ assert result.exit_code != 0
151
+ # Typer/Click writes the "Missing argument" message to stdout
152
+ # in their styled error panel; accept either stream.
153
+ combined = _strip_ansi(result.stdout + (result.stderr or ""))
154
+ assert "Missing argument" in combined or "WORKLOAD" in combined
155
+
156
+ def test_run_help_exits_zero(self, runner: CliRunner):
157
+ """`darwin run --help` always succeeds and prints usage."""
158
+ result = runner.invoke(app, ["run", "--help"])
159
+ assert result.exit_code == 0
160
+ assert "WORKLOAD" in _strip_ansi(result.stdout)
161
+ assert "--substrate" in _strip_ansi(result.stdout)
162
+ assert "--cost-cap" in _strip_ansi(result.stdout)
163
+
164
+ def test_run_help_lists_all_documented_flags(self, runner: CliRunner):
165
+ """All public flags appear in help output."""
166
+ result = runner.invoke(app, ["run", "--help"])
167
+ for flag in [
168
+ "--substrate",
169
+ "--language",
170
+ "--cost-cap",
171
+ "--timeout",
172
+ "--memory",
173
+ "--save",
174
+ "--json",
175
+ ]:
176
+ assert flag in _strip_ansi(result.stdout), f"flag {flag} missing from help"
177
+
178
+
179
+ # ---------------------------------------------------------------------------
180
+ # File vs inline workload resolution
181
+ # ---------------------------------------------------------------------------
182
+
183
+
184
+ class TestWorkloadResolution:
185
+ """The CLI's "is this arg a file or a code string" heuristic."""
186
+
187
+ def test_run_with_existing_file_reads_contents(
188
+ self, runner: CliRunner, mocked_run, hello_py: Path
189
+ ):
190
+ """When the workload arg is an existing file path, its contents are executed."""
191
+ result = runner.invoke(app, ["run", str(hello_py)])
192
+ assert result.exit_code == 0, result.stdout
193
+ # Verify the underlying call was made with the FILE CONTENTS, not the filename.
194
+ call_args = mocked_run.call_args
195
+ code_arg = call_args.kwargs.get("code") or (call_args.args[0] if call_args.args else None)
196
+ assert code_arg is not None
197
+ assert 'print("hello")' in code_arg
198
+ assert code_arg != str(hello_py), "CLI should read file contents, not pass the filename"
199
+
200
+ def test_run_with_inline_code_passes_string_directly(self, runner: CliRunner, mocked_run):
201
+ """When the workload arg isn't a file, it's treated as inline code."""
202
+ result = runner.invoke(app, ["run", 'print("inline")'])
203
+ assert result.exit_code == 0, result.stdout
204
+ call_args = mocked_run.call_args
205
+ code_arg = call_args.kwargs.get("code") or (call_args.args[0] if call_args.args else None)
206
+ assert code_arg == 'print("inline")'
207
+
208
+ def test_run_with_nonexistent_file_treats_as_inline(
209
+ self, runner: CliRunner, mocked_run, tmp_path: Path
210
+ ):
211
+ """A workload arg that LOOKS like a file path but doesn't exist falls back to inline."""
212
+ nonexistent = tmp_path / "does_not_exist.py"
213
+ runner.invoke(app, ["run", str(nonexistent)])
214
+ # CLI doesn't crash — it passes the string to the substrate, which will likely
215
+ # fail to execute. We check the CLI plumbing, not the substrate outcome.
216
+ # The important guarantee: we don't crash with FileNotFoundError on the CLI side.
217
+ call_args = mocked_run.call_args
218
+ if call_args is not None:
219
+ code_arg = call_args.kwargs.get("code") or (
220
+ call_args.args[0] if call_args.args else None
221
+ )
222
+ assert code_arg == str(nonexistent)
223
+
224
+
225
+ # ---------------------------------------------------------------------------
226
+ # Flag handling
227
+ # ---------------------------------------------------------------------------
228
+
229
+
230
+ class TestFlagHandling:
231
+ """Every flag exposed by `darwin run` is wired correctly."""
232
+
233
+ def test_substrate_flag_is_passed_through(self, runner: CliRunner, mocked_run, hello_py: Path):
234
+ result = runner.invoke(app, ["run", str(hello_py), "--substrate", "local-docker-v0"])
235
+ assert result.exit_code == 0, result.stdout
236
+ kwargs = mocked_run.call_args.kwargs
237
+ assert kwargs.get("substrate") == "local-docker-v0"
238
+
239
+ def test_cost_cap_flag_is_passed_through(self, runner: CliRunner, mocked_run, hello_py: Path):
240
+ result = runner.invoke(app, ["run", str(hello_py), "--cost-cap", "0.5"])
241
+ assert result.exit_code == 0, result.stdout
242
+ kwargs = mocked_run.call_args.kwargs
243
+ assert kwargs.get("cost_cap") == 0.5
244
+
245
+ def test_timeout_flag_is_passed_through(self, runner: CliRunner, mocked_run, hello_py: Path):
246
+ result = runner.invoke(app, ["run", str(hello_py), "--timeout", "60"])
247
+ assert result.exit_code == 0, result.stdout
248
+ kwargs = mocked_run.call_args.kwargs
249
+ assert kwargs.get("timeout") == 60
250
+
251
+ def test_memory_flag_is_passed_through(self, runner: CliRunner, mocked_run, hello_py: Path):
252
+ result = runner.invoke(app, ["run", str(hello_py), "--memory", "1024"])
253
+ assert result.exit_code == 0, result.stdout
254
+ kwargs = mocked_run.call_args.kwargs
255
+ assert kwargs.get("memory_mb") == 1024
256
+
257
+ def test_language_flag_is_passed_through(self, runner: CliRunner, mocked_run, hello_py: Path):
258
+ result = runner.invoke(app, ["run", str(hello_py), "--language", "node"])
259
+ assert result.exit_code == 0, result.stdout
260
+ kwargs = mocked_run.call_args.kwargs
261
+ assert kwargs.get("language") == "node"
262
+
263
+
264
+ # ---------------------------------------------------------------------------
265
+ # Output formats
266
+ # ---------------------------------------------------------------------------
267
+
268
+
269
+ class TestOutputFormats:
270
+ """The CLI's --save and --json flags produce correct artifacts."""
271
+
272
+ def test_save_flag_writes_attestation_to_disk(
273
+ self, runner: CliRunner, mocked_run, hello_py: Path, tmp_path: Path
274
+ ):
275
+ out_path = tmp_path / "att.json"
276
+ result = runner.invoke(app, ["run", str(hello_py), "--save", str(out_path)])
277
+ assert result.exit_code == 0, result.stdout
278
+ assert out_path.exists(), "—save target file was not created"
279
+ # The saved file must be valid JSON
280
+ saved = json.loads(out_path.read_text())
281
+ assert saved.get("attestation_id") == "test-attestation-id-0001"
282
+ assert saved.get("schema") == "darwin.cloud/agenticcloud/attestation/v0.2"
283
+
284
+ def test_json_flag_emits_parseable_json_only(
285
+ self, runner: CliRunner, mocked_run, hello_py: Path
286
+ ):
287
+ result = runner.invoke(app, ["run", str(hello_py), "--json"])
288
+ assert result.exit_code == 0, result.stdout
289
+ # In --json mode, stdout should be pure JSON (parseable)
290
+ parsed = json.loads(result.stdout)
291
+ assert parsed.get("attestation_id") == "test-attestation-id-0001"
292
+
293
+
294
+ # ---------------------------------------------------------------------------
295
+ # Smoke / regression
296
+ # ---------------------------------------------------------------------------
297
+
298
+
299
+ class TestSmoke:
300
+ """Basic invocation paths that have failed in production before."""
301
+
302
+ def test_run_with_file_does_not_raise(self, runner: CliRunner, mocked_run, hello_py: Path):
303
+ """Regression: `darwin run hello.py` from a directory where hello.py exists must complete."""
304
+ result = runner.invoke(app, ["run", str(hello_py)])
305
+ assert result.exception is None or result.exit_code == 0, (
306
+ f"unexpected exception: {result.exception!r}\nstdout: {result.stdout}"
307
+ )
308
+
309
+ def test_run_with_inline_code_does_not_raise(self, runner: CliRunner, mocked_run):
310
+ """Regression: `darwin run 'print(...)'` must complete."""
311
+ result = runner.invoke(app, ["run", 'print("ok")'])
312
+ assert result.exception is None or result.exit_code == 0, (
313
+ f"unexpected exception: {result.exception!r}\nstdout: {result.stdout}"
314
+ )
File without changes