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.
Files changed (58) hide show
  1. {palonexus-0.2.2 → palonexus-0.2.3}/PKG-INFO +1 -1
  2. palonexus-0.2.3/src/palonexus/_build.py +2 -0
  3. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_sdist_provenance.py +1 -1
  4. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_version.py +2 -2
  5. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/cli/commands.py +85 -0
  6. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/cli/main.py +14 -0
  7. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/client.py +273 -0
  8. palonexus-0.2.2/src/palonexus/_build.py +0 -2
  9. {palonexus-0.2.2 → palonexus-0.2.3}/.gitignore +0 -0
  10. {palonexus-0.2.2 → palonexus-0.2.3}/LICENSE +0 -0
  11. {palonexus-0.2.2 → palonexus-0.2.3}/hatch_build.py +0 -0
  12. {palonexus-0.2.2 → palonexus-0.2.3}/pyproject.toml +0 -0
  13. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/__init__.py +0 -0
  14. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_canonicalize.py +0 -0
  15. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_generated/__init__.py +0 -0
  16. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/_generated/protocol.py +0 -0
  17. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/approvals.py +0 -0
  18. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/async_client.py +0 -0
  19. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/cli/__init__.py +0 -0
  20. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/client.py +0 -0
  21. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/context.py +0 -0
  22. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/credentials.py +0 -0
  23. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/__init__.py +0 -0
  24. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/context.py +0 -0
  25. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/contracts.py +0 -0
  26. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/credentials.py +0 -0
  27. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/guard.py +0 -0
  28. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/runner.py +0 -0
  29. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/scaffold.py +0 -0
  30. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/__init__.py +0 -0
  31. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/__init__.py +0 -0
  32. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/agent.py +0 -0
  33. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/fixtures/release-change.json +0 -0
  34. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/palonexus-agent.yaml +0 -0
  35. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/palonexus-registration.yaml +0 -0
  36. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/templates/plain_python/pyproject.toml.tmpl +0 -0
  37. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/developer/version.py +0 -0
  38. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/errors.py +0 -0
  39. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/__init__.py +0 -0
  40. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/did.py +0 -0
  41. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/oidc.py +0 -0
  42. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/identity/vc.py +0 -0
  43. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/__init__.py +0 -0
  44. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/deepagents.py +0 -0
  45. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/langchain.py +0 -0
  46. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/integrations/langgraph.py +0 -0
  47. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/keystore.py +0 -0
  48. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/models.py +0 -0
  49. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/protocol.py +0 -0
  50. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/py.typed +0 -0
  51. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/redaction.py +0 -0
  52. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/retry.py +0 -0
  53. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/testing/__init__.py +0 -0
  54. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/testing/fake_transport.py +0 -0
  55. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/testing/mock_server.py +0 -0
  56. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/transports/__init__.py +0 -0
  57. {palonexus-0.2.2 → palonexus-0.2.3}/src/palonexus/transports/base.py +0 -0
  58. {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.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
@@ -0,0 +1,2 @@
1
+ # generated by the PaloNexus build hook
2
+ SOURCE_REVISION = "39ef113f4586132b2f647567be91f2723cde3435"
@@ -1,3 +1,3 @@
1
1
  # generated by the PaloNexus build hook
2
2
  SDIST_MARKER = "palonexus-sdk-sdist-v1"
3
- SOURCE_REVISION = "90b32d14d89ad419ce5ee7a4b3cee5ef3f6db5ce"
3
+ SOURCE_REVISION = "39ef113f4586132b2f647567be91f2723cde3435"
@@ -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.2'
22
- __version_tuple__ = version_tuple = (0, 2, 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],
@@ -1,2 +0,0 @@
1
- # generated by the PaloNexus build hook
2
- SOURCE_REVISION = "90b32d14d89ad419ce5ee7a4b3cee5ef3f6db5ce"
File without changes
File without changes
File without changes
File without changes