palonexus 0.2.2__tar.gz → 0.2.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.
- {palonexus-0.2.2 → palonexus-0.2.3}/PKG-INFO +1 -1
- palonexus-0.2.3/src/palonexus/_build.py +2 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_sdist_provenance.py +1 -1
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_version.py +2 -2
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/cli/commands.py +85 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/cli/main.py +14 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/client.py +273 -0
- palonexus-0.2.2/src/palonexus/_build.py +0 -2
- {palonexus-0.2.2 → palonexus-0.2.3}/.gitignore +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/LICENSE +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/hatch_build.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/pyproject.toml +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_canonicalize.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_generated/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_generated/protocol.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/approvals.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/async_client.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/cli/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/client.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/context.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/credentials.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/context.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/contracts.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/credentials.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/guard.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/runner.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/scaffold.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/agent.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/fixtures/release-change.json +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/palonexus-agent.yaml +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/palonexus-registration.yaml +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/pyproject.toml.tmpl +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/version.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/errors.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/did.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/oidc.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/vc.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/deepagents.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/langchain.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/langgraph.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/keystore.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/models.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/protocol.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/py.typed +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/redaction.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/retry.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/testing/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/testing/fake_transport.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/testing/mock_server.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/transports/__init__.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/transports/base.py +0 -0
- {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/transports/http.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: palonexus
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Python SDK for the PaloNexus agent control plane
|
|
5
5
|
Project-URL: Homepage, https://github.com/rogerchucker/palonexus-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/rogerchucker/palonexus-sdk
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.3'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 3)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -22,6 +22,7 @@ from packaging.version import InvalidVersion, Version
|
|
|
22
22
|
from palonexus.developer.client import (
|
|
23
23
|
DeveloperClient,
|
|
24
24
|
DeveloperClientError,
|
|
25
|
+
RequestRejected,
|
|
25
26
|
_origin,
|
|
26
27
|
_registration_authority_profile,
|
|
27
28
|
canonical_json,
|
|
@@ -379,6 +380,36 @@ def _project_client(
|
|
|
379
380
|
return client, store, session, agent, descriptor, credential_name
|
|
380
381
|
|
|
381
382
|
|
|
383
|
+
def _claim_project_client(
|
|
384
|
+
args: Namespace,
|
|
385
|
+
) -> tuple[DeveloperClient, CredentialStore, dict[str, str], dict[str, Any]]:
|
|
386
|
+
try:
|
|
387
|
+
store = credential_store(
|
|
388
|
+
allow_file_fallback=getattr(args, "allow_file_credential_store", False)
|
|
389
|
+
)
|
|
390
|
+
session = store.load("session")
|
|
391
|
+
descriptor = _project_descriptor()
|
|
392
|
+
if session is None:
|
|
393
|
+
raise CommandError("pnxs login is required")
|
|
394
|
+
tenant_id = session.get("tenant_id")
|
|
395
|
+
owner = session.get("owner_subject")
|
|
396
|
+
if (
|
|
397
|
+
not isinstance(tenant_id, str)
|
|
398
|
+
or not tenant_id
|
|
399
|
+
or not isinstance(owner, str)
|
|
400
|
+
or not owner
|
|
401
|
+
):
|
|
402
|
+
raise CommandError("stored developer session is invalid")
|
|
403
|
+
client = DeveloperClient(
|
|
404
|
+
os.environ.get("PNXS_API_URL", "https://api.palonexus.cloud")
|
|
405
|
+
)
|
|
406
|
+
except CredentialStoreUnavailable as error:
|
|
407
|
+
raise CommandError(f"credential access failed: {error}") from error
|
|
408
|
+
except DeveloperClientError as error:
|
|
409
|
+
raise CommandError(f"tenant API configuration failed: {error}") from error
|
|
410
|
+
return client, store, session, descriptor
|
|
411
|
+
|
|
412
|
+
|
|
382
413
|
def _print_json(value: dict[str, Any]) -> None:
|
|
383
414
|
print(json.dumps(value, sort_keys=True, separators=(",", ":")))
|
|
384
415
|
|
|
@@ -622,6 +653,14 @@ def agents_register(args: Namespace) -> int:
|
|
|
622
653
|
agent["agent_generation"] = str(result["generation"])
|
|
623
654
|
agent["registered_descriptor_digest"] = str(result["descriptor_digest"])
|
|
624
655
|
store.save(credential_name, agent)
|
|
656
|
+
except RequestRejected as error:
|
|
657
|
+
if error.status_code == 409:
|
|
658
|
+
raise CommandError(
|
|
659
|
+
"agent registration failed: the agent already exists or conflicts. "
|
|
660
|
+
"For a web-created agent, use "
|
|
661
|
+
f"`pnxs agent attach {descriptor['name']}`."
|
|
662
|
+
) from error
|
|
663
|
+
raise CommandError(f"agent registration failed: {error}") from error
|
|
625
664
|
except (
|
|
626
665
|
DeveloperClientError,
|
|
627
666
|
CredentialStoreUnavailable,
|
|
@@ -638,6 +677,52 @@ def agents_register(args: Namespace) -> int:
|
|
|
638
677
|
return 0
|
|
639
678
|
|
|
640
679
|
|
|
680
|
+
def agent_attach(args: Namespace) -> int:
|
|
681
|
+
_require_standalone_registration_cli()
|
|
682
|
+
client, store, session, descriptor = _claim_project_client(args)
|
|
683
|
+
if args.name != descriptor["name"]:
|
|
684
|
+
raise CommandError(
|
|
685
|
+
f"agent name {args.name!r} does not match the local descriptor name "
|
|
686
|
+
f"{descriptor['name']!r}"
|
|
687
|
+
)
|
|
688
|
+
tenant_id = str(session["tenant_id"])
|
|
689
|
+
credential_name = f"agent:{tenant_id}:{args.name}"
|
|
690
|
+
try:
|
|
691
|
+
cli_version = installed_sdk_version()
|
|
692
|
+
client.require_cli_compatibility(session, cli_version)
|
|
693
|
+
store.create_if_absent(credential_name, generate_agent_credential())
|
|
694
|
+
agent = store.load(credential_name)
|
|
695
|
+
if agent is None:
|
|
696
|
+
raise CredentialStoreUnavailable("agent credential was not persisted")
|
|
697
|
+
result = client.attach_agent(
|
|
698
|
+
session, agent, descriptor, cli_version=cli_version
|
|
699
|
+
)
|
|
700
|
+
agent["agent_id"] = str(result["agent_id"])
|
|
701
|
+
agent["agent_generation"] = str(result["generation"])
|
|
702
|
+
agent["registered_descriptor_digest"] = str(result["descriptor_digest"])
|
|
703
|
+
agent["claim_id"] = str(result["claim_id"])
|
|
704
|
+
store.save(credential_name, agent)
|
|
705
|
+
except (
|
|
706
|
+
DeveloperClientError,
|
|
707
|
+
CredentialStoreUnavailable,
|
|
708
|
+
KeyError,
|
|
709
|
+
ScaffoldError,
|
|
710
|
+
) as error:
|
|
711
|
+
raise CommandError(f"agent attach failed: {error}") from error
|
|
712
|
+
_print_json(result)
|
|
713
|
+
return 0
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def register_agent(args: Namespace) -> int:
|
|
717
|
+
descriptor = _project_descriptor()
|
|
718
|
+
if args.name != descriptor["name"]:
|
|
719
|
+
raise CommandError(
|
|
720
|
+
f"agent name {args.name!r} does not match the local descriptor name "
|
|
721
|
+
f"{descriptor['name']!r}"
|
|
722
|
+
)
|
|
723
|
+
return agents_register(args)
|
|
724
|
+
|
|
725
|
+
|
|
641
726
|
def _new_ceiling_request(
|
|
642
727
|
agent: dict[str, str], descriptor: dict[str, Any]
|
|
643
728
|
) -> tuple[str, dict[str, Any]]:
|
|
@@ -49,6 +49,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
49
49
|
command.add_argument("--allow-file-credential-store", action="store_true")
|
|
50
50
|
command.set_defaults(handler=handler)
|
|
51
51
|
|
|
52
|
+
agent = subcommands.add_parser("agent", allow_abbrev=False)
|
|
53
|
+
singular_agent_commands = agent.add_subparsers(
|
|
54
|
+
dest="singular_agent_command", required=True
|
|
55
|
+
)
|
|
56
|
+
attach = singular_agent_commands.add_parser("attach", allow_abbrev=False)
|
|
57
|
+
attach.add_argument("name")
|
|
58
|
+
attach.add_argument("--allow-file-credential-store", action="store_true")
|
|
59
|
+
attach.set_defaults(handler=commands.agent_attach)
|
|
60
|
+
|
|
61
|
+
register = subcommands.add_parser("register", allow_abbrev=False)
|
|
62
|
+
register.add_argument("name")
|
|
63
|
+
register.add_argument("--allow-file-credential-store", action="store_true")
|
|
64
|
+
register.set_defaults(handler=commands.register_agent)
|
|
65
|
+
|
|
52
66
|
run = subcommands.add_parser("run", allow_abbrev=False)
|
|
53
67
|
run.add_argument("agent_file")
|
|
54
68
|
run.add_argument("--input", required=True)
|
|
@@ -381,6 +381,31 @@ _REGISTRATION_RESPONSE_FIELDS = {
|
|
|
381
381
|
"not_before",
|
|
382
382
|
"expires_at",
|
|
383
383
|
}
|
|
384
|
+
_CLAIM_CHALLENGE_FIELDS = {
|
|
385
|
+
"schema_version",
|
|
386
|
+
"challenge_id",
|
|
387
|
+
"agent_id",
|
|
388
|
+
"tenant_id",
|
|
389
|
+
"accountable_owner",
|
|
390
|
+
"generation",
|
|
391
|
+
"descriptor_digest",
|
|
392
|
+
"key_thumbprint",
|
|
393
|
+
"nonce",
|
|
394
|
+
"expires_at",
|
|
395
|
+
"status",
|
|
396
|
+
}
|
|
397
|
+
_CLAIM_RECEIPT_FIELDS = {
|
|
398
|
+
"schema_version",
|
|
399
|
+
"claim_id",
|
|
400
|
+
"agent_id",
|
|
401
|
+
"tenant_id",
|
|
402
|
+
"accountable_owner",
|
|
403
|
+
"descriptor_digest",
|
|
404
|
+
"key_thumbprint",
|
|
405
|
+
"generation",
|
|
406
|
+
"status",
|
|
407
|
+
"claimed_at",
|
|
408
|
+
}
|
|
384
409
|
_REVOCATION_RESPONSE_FIELDS = {
|
|
385
410
|
"schema_version",
|
|
386
411
|
"event_id",
|
|
@@ -602,6 +627,95 @@ def _registration_binding(
|
|
|
602
627
|
return jwk, thumbprint, name, digest, authority_profile
|
|
603
628
|
|
|
604
629
|
|
|
630
|
+
def _claim_binding(
|
|
631
|
+
agent: dict[str, str], descriptor: dict[str, Any]
|
|
632
|
+
) -> tuple[dict[str, str], str, str, str]:
|
|
633
|
+
try:
|
|
634
|
+
jwk = json.loads(
|
|
635
|
+
_require_string(agent.get("public_key_jwk"), "agent public key")
|
|
636
|
+
)
|
|
637
|
+
except json.JSONDecodeError as error:
|
|
638
|
+
raise ProtocolError("invalid agent public key") from error
|
|
639
|
+
if (
|
|
640
|
+
not isinstance(jwk, dict)
|
|
641
|
+
or set(jwk) != {"kty", "crv", "x"}
|
|
642
|
+
or jwk.get("kty") != "OKP"
|
|
643
|
+
or jwk.get("crv") != "Ed25519"
|
|
644
|
+
or not all(isinstance(value, str) for value in jwk.values())
|
|
645
|
+
):
|
|
646
|
+
raise ProtocolError("invalid agent public key")
|
|
647
|
+
thumbprint = hashlib.sha256(canonical_json(jwk)).hexdigest()
|
|
648
|
+
name = _require_string(descriptor.get("name"), "agent name")
|
|
649
|
+
digest = _require_string(descriptor.get("descriptor_digest"), "descriptor digest")
|
|
650
|
+
if re.fullmatch(r"[0-9a-f]{64}", digest) is None:
|
|
651
|
+
raise ProtocolError("invalid descriptor digest")
|
|
652
|
+
return jwk, thumbprint, name, digest
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def _validate_claim_challenge(
|
|
656
|
+
response: dict[str, Any],
|
|
657
|
+
*,
|
|
658
|
+
session: dict[str, str],
|
|
659
|
+
name: str,
|
|
660
|
+
descriptor_digest: str,
|
|
661
|
+
key_thumbprint: str,
|
|
662
|
+
) -> dict[str, Any]:
|
|
663
|
+
if set(response) != _CLAIM_CHALLENGE_FIELDS:
|
|
664
|
+
raise ProtocolError("invalid agent claim challenge shape")
|
|
665
|
+
expected = {
|
|
666
|
+
"schema_version": "palonexus.developer-agent-claim-challenge/v1",
|
|
667
|
+
"agent_id": name,
|
|
668
|
+
"tenant_id": _require_string(session.get("tenant_id"), "tenant_id"),
|
|
669
|
+
"accountable_owner": _require_string(
|
|
670
|
+
session.get("owner_subject"), "owner_subject"
|
|
671
|
+
),
|
|
672
|
+
"descriptor_digest": descriptor_digest,
|
|
673
|
+
"key_thumbprint": key_thumbprint,
|
|
674
|
+
"status": "pending",
|
|
675
|
+
}
|
|
676
|
+
if any(response.get(field) != value for field, value in expected.items()):
|
|
677
|
+
raise ProtocolError("agent claim challenge is not bound to the request")
|
|
678
|
+
challenge_id = _require_string(response.get("challenge_id"), "challenge ID")
|
|
679
|
+
if re.fullmatch(r"claim-[0-9a-f]{32}", challenge_id) is None:
|
|
680
|
+
raise ProtocolError("invalid agent claim challenge ID")
|
|
681
|
+
nonce = _require_string(response.get("nonce"), "claim nonce")
|
|
682
|
+
if re.fullmatch(r"[A-Za-z0-9_-]{43}", nonce) is None:
|
|
683
|
+
raise ProtocolError("invalid agent claim nonce")
|
|
684
|
+
_require_positive_int(response.get("generation"), "generation")
|
|
685
|
+
_require_timestamp(response.get("expires_at"), "claim expiry")
|
|
686
|
+
return response
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def _validate_claim_receipt(
|
|
690
|
+
response: dict[str, Any],
|
|
691
|
+
*,
|
|
692
|
+
session: dict[str, str],
|
|
693
|
+
name: str,
|
|
694
|
+
descriptor_digest: str,
|
|
695
|
+
key_thumbprint: str,
|
|
696
|
+
challenge_id: str,
|
|
697
|
+
) -> dict[str, Any]:
|
|
698
|
+
if set(response) != _CLAIM_RECEIPT_FIELDS:
|
|
699
|
+
raise ProtocolError("invalid agent claim receipt shape")
|
|
700
|
+
expected = {
|
|
701
|
+
"schema_version": "palonexus.developer-agent-claim/v1",
|
|
702
|
+
"claim_id": challenge_id,
|
|
703
|
+
"agent_id": name,
|
|
704
|
+
"tenant_id": _require_string(session.get("tenant_id"), "tenant_id"),
|
|
705
|
+
"accountable_owner": _require_string(
|
|
706
|
+
session.get("owner_subject"), "owner_subject"
|
|
707
|
+
),
|
|
708
|
+
"descriptor_digest": descriptor_digest,
|
|
709
|
+
"key_thumbprint": key_thumbprint,
|
|
710
|
+
"status": "attached",
|
|
711
|
+
}
|
|
712
|
+
if any(response.get(field) != value for field, value in expected.items()):
|
|
713
|
+
raise ProtocolError("agent claim receipt is not bound to the request")
|
|
714
|
+
_require_positive_int(response.get("generation"), "generation")
|
|
715
|
+
_require_timestamp(response.get("claimed_at"), "claimed_at")
|
|
716
|
+
return response
|
|
717
|
+
|
|
718
|
+
|
|
605
719
|
def _validate_revocation_response(
|
|
606
720
|
response: dict[str, Any],
|
|
607
721
|
*,
|
|
@@ -1138,6 +1252,165 @@ class DeveloperClient:
|
|
|
1138
1252
|
authority_profile=authority_profile,
|
|
1139
1253
|
)
|
|
1140
1254
|
|
|
1255
|
+
def attach_agent(
|
|
1256
|
+
self,
|
|
1257
|
+
session: dict[str, str],
|
|
1258
|
+
agent: dict[str, str],
|
|
1259
|
+
descriptor: dict[str, Any],
|
|
1260
|
+
*,
|
|
1261
|
+
cli_version: str | None = None,
|
|
1262
|
+
) -> dict[str, Any]:
|
|
1263
|
+
"""Bind a locally generated proof key to an owner-matched web registration."""
|
|
1264
|
+
private = _decode_private_key(agent.get("private_key"))
|
|
1265
|
+
jwk, thumbprint, name, digest = _claim_binding(agent, descriptor)
|
|
1266
|
+
token = _require_string(session.get("session_token"), "session token")
|
|
1267
|
+
metadata = (
|
|
1268
|
+
{
|
|
1269
|
+
"Palonexus-CLI-Contract": CLI_CONTRACT,
|
|
1270
|
+
"Palonexus-CLI-Version": _require_string(
|
|
1271
|
+
cli_version, "installed CLI version"
|
|
1272
|
+
),
|
|
1273
|
+
}
|
|
1274
|
+
if cli_version is not None
|
|
1275
|
+
else {}
|
|
1276
|
+
)
|
|
1277
|
+
request_value = {
|
|
1278
|
+
"schema_version": "palonexus.developer-agent-claim-request/v1",
|
|
1279
|
+
"descriptor_digest": digest,
|
|
1280
|
+
"public_key_jwk": jwk,
|
|
1281
|
+
}
|
|
1282
|
+
request_body = canonical_json(request_value)
|
|
1283
|
+
request_idempotency = hashlib.sha256(request_body).hexdigest()
|
|
1284
|
+
request_digest = hashlib.sha256(
|
|
1285
|
+
canonical_json(
|
|
1286
|
+
{
|
|
1287
|
+
"body": request_value,
|
|
1288
|
+
"idempotency_key": request_idempotency,
|
|
1289
|
+
}
|
|
1290
|
+
)
|
|
1291
|
+
).hexdigest()
|
|
1292
|
+
expected_challenge_id = (
|
|
1293
|
+
"claim-"
|
|
1294
|
+
+ hashlib.sha256(
|
|
1295
|
+
canonical_json(
|
|
1296
|
+
{
|
|
1297
|
+
"tenant_id": _require_string(
|
|
1298
|
+
session.get("tenant_id"), "tenant_id"
|
|
1299
|
+
),
|
|
1300
|
+
"agent_id": name,
|
|
1301
|
+
"accountable_owner": _require_string(
|
|
1302
|
+
session.get("owner_subject"), "owner_subject"
|
|
1303
|
+
),
|
|
1304
|
+
"request_digest": request_digest,
|
|
1305
|
+
}
|
|
1306
|
+
)
|
|
1307
|
+
).hexdigest()[:32]
|
|
1308
|
+
)
|
|
1309
|
+
base_path = "/v1/developer/agents/" + quote(name, safe="") + "/claim-challenges"
|
|
1310
|
+
status_path = base_path + "/" + quote(expected_challenge_id, safe="")
|
|
1311
|
+
try:
|
|
1312
|
+
challenge_response = self._request(
|
|
1313
|
+
"POST",
|
|
1314
|
+
base_path,
|
|
1315
|
+
body=request_body,
|
|
1316
|
+
headers={
|
|
1317
|
+
"Authorization": "Bearer " + token,
|
|
1318
|
+
"Idempotency-Key": request_idempotency,
|
|
1319
|
+
**metadata,
|
|
1320
|
+
},
|
|
1321
|
+
expected=201,
|
|
1322
|
+
allowed_fields=_CLAIM_CHALLENGE_FIELDS,
|
|
1323
|
+
)
|
|
1324
|
+
except DeveloperClientError as original:
|
|
1325
|
+
try:
|
|
1326
|
+
recovered = self._request(
|
|
1327
|
+
"GET",
|
|
1328
|
+
status_path,
|
|
1329
|
+
headers={"Authorization": "Bearer " + token, **metadata},
|
|
1330
|
+
allowed_fields=_CLAIM_CHALLENGE_FIELDS | _CLAIM_RECEIPT_FIELDS,
|
|
1331
|
+
)
|
|
1332
|
+
except DeveloperClientError:
|
|
1333
|
+
raise original from None
|
|
1334
|
+
if set(recovered) == _CLAIM_RECEIPT_FIELDS:
|
|
1335
|
+
return _validate_claim_receipt(
|
|
1336
|
+
recovered,
|
|
1337
|
+
session=session,
|
|
1338
|
+
name=name,
|
|
1339
|
+
descriptor_digest=digest,
|
|
1340
|
+
key_thumbprint=thumbprint,
|
|
1341
|
+
challenge_id=expected_challenge_id,
|
|
1342
|
+
)
|
|
1343
|
+
challenge_response = recovered
|
|
1344
|
+
challenge = _validate_claim_challenge(
|
|
1345
|
+
challenge_response,
|
|
1346
|
+
session=session,
|
|
1347
|
+
name=name,
|
|
1348
|
+
descriptor_digest=digest,
|
|
1349
|
+
key_thumbprint=thumbprint,
|
|
1350
|
+
)
|
|
1351
|
+
challenge_id = str(challenge["challenge_id"])
|
|
1352
|
+
if challenge_id != expected_challenge_id:
|
|
1353
|
+
raise ProtocolError("agent claim challenge has an unexpected ID")
|
|
1354
|
+
message = canonical_json(
|
|
1355
|
+
{
|
|
1356
|
+
"accountable_owner": challenge["accountable_owner"],
|
|
1357
|
+
"agent_id": challenge["agent_id"],
|
|
1358
|
+
"challenge_id": challenge_id,
|
|
1359
|
+
"descriptor_digest": challenge["descriptor_digest"],
|
|
1360
|
+
"expires_at": challenge["expires_at"],
|
|
1361
|
+
"generation": challenge["generation"],
|
|
1362
|
+
"key_thumbprint": challenge["key_thumbprint"],
|
|
1363
|
+
"nonce": challenge["nonce"],
|
|
1364
|
+
"purpose": "palonexus.developer-agent-claim.v1",
|
|
1365
|
+
"tenant_id": challenge["tenant_id"],
|
|
1366
|
+
}
|
|
1367
|
+
)
|
|
1368
|
+
completion_body = canonical_json(
|
|
1369
|
+
{
|
|
1370
|
+
"schema_version": "palonexus.developer-agent-claim-completion/v1",
|
|
1371
|
+
"challenge_id": challenge_id,
|
|
1372
|
+
"nonce": challenge["nonce"],
|
|
1373
|
+
"proof": {
|
|
1374
|
+
"alg": "EdDSA",
|
|
1375
|
+
"key_thumbprint": thumbprint,
|
|
1376
|
+
"signature": _b64url(private.sign(message)),
|
|
1377
|
+
},
|
|
1378
|
+
}
|
|
1379
|
+
)
|
|
1380
|
+
completion_path = base_path + "/" + quote(challenge_id, safe="") + "/complete"
|
|
1381
|
+
try:
|
|
1382
|
+
response = self._request(
|
|
1383
|
+
"POST",
|
|
1384
|
+
completion_path,
|
|
1385
|
+
body=completion_body,
|
|
1386
|
+
headers={
|
|
1387
|
+
"Authorization": "Bearer " + token,
|
|
1388
|
+
"Idempotency-Key": hashlib.sha256(completion_body).hexdigest(),
|
|
1389
|
+
**metadata,
|
|
1390
|
+
},
|
|
1391
|
+
allowed_fields=_CLAIM_RECEIPT_FIELDS,
|
|
1392
|
+
)
|
|
1393
|
+
except DeveloperClientError as original:
|
|
1394
|
+
try:
|
|
1395
|
+
response = self._request(
|
|
1396
|
+
"GET",
|
|
1397
|
+
status_path,
|
|
1398
|
+
headers={"Authorization": "Bearer " + token, **metadata},
|
|
1399
|
+
allowed_fields=_CLAIM_CHALLENGE_FIELDS | _CLAIM_RECEIPT_FIELDS,
|
|
1400
|
+
)
|
|
1401
|
+
if set(response) != _CLAIM_RECEIPT_FIELDS:
|
|
1402
|
+
raise original
|
|
1403
|
+
except DeveloperClientError:
|
|
1404
|
+
raise original from None
|
|
1405
|
+
return _validate_claim_receipt(
|
|
1406
|
+
response,
|
|
1407
|
+
session=session,
|
|
1408
|
+
name=name,
|
|
1409
|
+
descriptor_digest=digest,
|
|
1410
|
+
key_thumbprint=thumbprint,
|
|
1411
|
+
challenge_id=challenge_id,
|
|
1412
|
+
)
|
|
1413
|
+
|
|
1141
1414
|
def reconcile_agent_registration(
|
|
1142
1415
|
self,
|
|
1143
1416
|
session: dict[str, str],
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/__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
|
|
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
|