octarin-cli 0.3.1 → 0.3.2
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.
package/assets/backfill.py
CHANGED
|
@@ -44,6 +44,7 @@ import getpass
|
|
|
44
44
|
import json
|
|
45
45
|
import os
|
|
46
46
|
import re
|
|
47
|
+
import ssl
|
|
47
48
|
import subprocess
|
|
48
49
|
import sys
|
|
49
50
|
import urllib.error
|
|
@@ -940,6 +941,49 @@ def _filter_spans_by_cutoff(event: dict, cutoff: datetime | None) -> dict | None
|
|
|
940
941
|
return event
|
|
941
942
|
|
|
942
943
|
|
|
944
|
+
_SSL_CTX: ssl.SSLContext | None = None
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
def _ssl_context() -> ssl.SSLContext:
|
|
948
|
+
"""A cert-verifying TLS context that also works on Pythons whose default
|
|
949
|
+
trust store is empty — the cause of ``CERTIFICATE_VERIFY_FAILED: unable to
|
|
950
|
+
get local issuer certificate`` on many macOS (python.org) installs. Resolves
|
|
951
|
+
a CA bundle, all options verifying: ``OCTARIN_CA_BUNDLE`` / ``SSL_CERT_FILE``
|
|
952
|
+
env, then ``certifi`` if importable, then known system bundles, then the
|
|
953
|
+
interpreter default. Built once and reused.
|
|
954
|
+
"""
|
|
955
|
+
global _SSL_CTX
|
|
956
|
+
if _SSL_CTX is not None:
|
|
957
|
+
return _SSL_CTX
|
|
958
|
+
cafile: str | None = None
|
|
959
|
+
for env in ("OCTARIN_CA_BUNDLE", "SSL_CERT_FILE"):
|
|
960
|
+
p = os.environ.get(env)
|
|
961
|
+
if p and os.path.exists(p):
|
|
962
|
+
cafile = p
|
|
963
|
+
break
|
|
964
|
+
if cafile is None:
|
|
965
|
+
try:
|
|
966
|
+
import certifi # noqa: PLC0415 - optional, used only if present
|
|
967
|
+
|
|
968
|
+
cafile = certifi.where()
|
|
969
|
+
except Exception:
|
|
970
|
+
for p in (
|
|
971
|
+
"/etc/ssl/cert.pem", # macOS base system, many Linux
|
|
972
|
+
"/opt/homebrew/etc/openssl@3/cert.pem", # Apple-silicon Homebrew
|
|
973
|
+
"/usr/local/etc/openssl@3/cert.pem", # Intel Homebrew
|
|
974
|
+
"/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu
|
|
975
|
+
"/etc/pki/tls/certs/ca-bundle.crt", # RHEL/CentOS
|
|
976
|
+
):
|
|
977
|
+
if os.path.exists(p):
|
|
978
|
+
cafile = p
|
|
979
|
+
break
|
|
980
|
+
try:
|
|
981
|
+
_SSL_CTX = ssl.create_default_context(cafile=cafile)
|
|
982
|
+
except Exception:
|
|
983
|
+
_SSL_CTX = ssl.create_default_context()
|
|
984
|
+
return _SSL_CTX
|
|
985
|
+
|
|
986
|
+
|
|
943
987
|
def post_event(
|
|
944
988
|
url: str, key: str, event: dict, timeout: float = 20.0
|
|
945
989
|
) -> tuple[bool, str]:
|
|
@@ -955,7 +999,7 @@ def post_event(
|
|
|
955
999
|
method="POST",
|
|
956
1000
|
)
|
|
957
1001
|
try:
|
|
958
|
-
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
1002
|
+
with urllib.request.urlopen(req, timeout=timeout, context=_ssl_context()) as resp:
|
|
959
1003
|
raw = resp.read().decode("utf-8", "replace")
|
|
960
1004
|
try:
|
|
961
1005
|
n = json.loads(raw).get("span_count")
|
|
@@ -24,6 +24,7 @@ import getpass
|
|
|
24
24
|
import hashlib
|
|
25
25
|
import json
|
|
26
26
|
import os
|
|
27
|
+
import ssl
|
|
27
28
|
import subprocess
|
|
28
29
|
import sys
|
|
29
30
|
import time
|
|
@@ -476,6 +477,46 @@ def user_ref() -> str:
|
|
|
476
477
|
return "unknown"
|
|
477
478
|
|
|
478
479
|
|
|
480
|
+
_SSL_CTX = None
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _ssl_context():
|
|
484
|
+
"""Cert-verifying TLS context resilient to empty trust stores (the macOS
|
|
485
|
+
python.org ``CERTIFICATE_VERIFY_FAILED`` issue). Resolves a CA bundle —
|
|
486
|
+
OCTARIN_CA_BUNDLE / SSL_CERT_FILE env, certifi if importable, known system
|
|
487
|
+
bundles, else the interpreter default — and reuses it."""
|
|
488
|
+
global _SSL_CTX
|
|
489
|
+
if _SSL_CTX is not None:
|
|
490
|
+
return _SSL_CTX
|
|
491
|
+
cafile = None
|
|
492
|
+
for env in ("OCTARIN_CA_BUNDLE", "SSL_CERT_FILE"):
|
|
493
|
+
p = os.environ.get(env)
|
|
494
|
+
if p and os.path.exists(p):
|
|
495
|
+
cafile = p
|
|
496
|
+
break
|
|
497
|
+
if cafile is None:
|
|
498
|
+
try:
|
|
499
|
+
import certifi
|
|
500
|
+
|
|
501
|
+
cafile = certifi.where()
|
|
502
|
+
except Exception:
|
|
503
|
+
for p in (
|
|
504
|
+
"/etc/ssl/cert.pem",
|
|
505
|
+
"/opt/homebrew/etc/openssl@3/cert.pem",
|
|
506
|
+
"/usr/local/etc/openssl@3/cert.pem",
|
|
507
|
+
"/etc/ssl/certs/ca-certificates.crt",
|
|
508
|
+
"/etc/pki/tls/certs/ca-bundle.crt",
|
|
509
|
+
):
|
|
510
|
+
if os.path.exists(p):
|
|
511
|
+
cafile = p
|
|
512
|
+
break
|
|
513
|
+
try:
|
|
514
|
+
_SSL_CTX = ssl.create_default_context(cafile=cafile)
|
|
515
|
+
except Exception:
|
|
516
|
+
_SSL_CTX = ssl.create_default_context()
|
|
517
|
+
return _SSL_CTX
|
|
518
|
+
|
|
519
|
+
|
|
479
520
|
def post_event(event: dict) -> bool:
|
|
480
521
|
"""POST the IngestEvent. Returns True on 2xx, False otherwise (fail-open)."""
|
|
481
522
|
url = os.environ.get("OCTARIN_INGEST_URL")
|
|
@@ -491,7 +532,7 @@ def post_event(event: dict) -> bool:
|
|
|
491
532
|
if api_key:
|
|
492
533
|
req.add_header("Authorization", f"Bearer {api_key}")
|
|
493
534
|
try:
|
|
494
|
-
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_S) as resp:
|
|
535
|
+
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_S, context=_ssl_context()) as resp:
|
|
495
536
|
return 200 <= resp.status < 300
|
|
496
537
|
except Exception:
|
|
497
538
|
return False
|
|
@@ -42,6 +42,7 @@ import getpass
|
|
|
42
42
|
import hashlib
|
|
43
43
|
import json
|
|
44
44
|
import os
|
|
45
|
+
import ssl
|
|
45
46
|
import subprocess
|
|
46
47
|
import sys
|
|
47
48
|
import time
|
|
@@ -547,6 +548,46 @@ def _notify_auth_required_once(project: str) -> None:
|
|
|
547
548
|
)
|
|
548
549
|
|
|
549
550
|
|
|
551
|
+
_SSL_CTX = None
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def _ssl_context():
|
|
555
|
+
"""Cert-verifying TLS context resilient to empty trust stores (the macOS
|
|
556
|
+
python.org ``CERTIFICATE_VERIFY_FAILED`` issue). Resolves a CA bundle —
|
|
557
|
+
OCTARIN_CA_BUNDLE / SSL_CERT_FILE env, certifi if importable, known system
|
|
558
|
+
bundles, else the interpreter default — and reuses it."""
|
|
559
|
+
global _SSL_CTX
|
|
560
|
+
if _SSL_CTX is not None:
|
|
561
|
+
return _SSL_CTX
|
|
562
|
+
cafile = None
|
|
563
|
+
for env in ("OCTARIN_CA_BUNDLE", "SSL_CERT_FILE"):
|
|
564
|
+
p = os.environ.get(env)
|
|
565
|
+
if p and os.path.exists(p):
|
|
566
|
+
cafile = p
|
|
567
|
+
break
|
|
568
|
+
if cafile is None:
|
|
569
|
+
try:
|
|
570
|
+
import certifi
|
|
571
|
+
|
|
572
|
+
cafile = certifi.where()
|
|
573
|
+
except Exception:
|
|
574
|
+
for p in (
|
|
575
|
+
"/etc/ssl/cert.pem",
|
|
576
|
+
"/opt/homebrew/etc/openssl@3/cert.pem",
|
|
577
|
+
"/usr/local/etc/openssl@3/cert.pem",
|
|
578
|
+
"/etc/ssl/certs/ca-certificates.crt",
|
|
579
|
+
"/etc/pki/tls/certs/ca-bundle.crt",
|
|
580
|
+
):
|
|
581
|
+
if os.path.exists(p):
|
|
582
|
+
cafile = p
|
|
583
|
+
break
|
|
584
|
+
try:
|
|
585
|
+
_SSL_CTX = ssl.create_default_context(cafile=cafile)
|
|
586
|
+
except Exception:
|
|
587
|
+
_SSL_CTX = ssl.create_default_context()
|
|
588
|
+
return _SSL_CTX
|
|
589
|
+
|
|
590
|
+
|
|
550
591
|
def post_event(event: dict) -> bool:
|
|
551
592
|
"""POST the IngestEvent. Returns True on 2xx, False otherwise (fail-open).
|
|
552
593
|
|
|
@@ -585,7 +626,7 @@ def post_event(event: dict) -> bool:
|
|
|
585
626
|
elif project:
|
|
586
627
|
req.add_header("X-Octarin-Project", project)
|
|
587
628
|
try:
|
|
588
|
-
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_S) as resp:
|
|
629
|
+
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_S, context=_ssl_context()) as resp:
|
|
589
630
|
return HTTP_OK <= resp.status < HTTP_MULTIPLE_CHOICES
|
|
590
631
|
except urllib.error.HTTPError as exc:
|
|
591
632
|
# Strict-auth signal from the server: print the login.sh hint, once.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "octarin-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Octarin's per-user CLI: install AI-coding capture (`octarin init` / `init-repo`) and authorize a machine (`octarin login`). Streams your Claude Code / Cursor / Codex usage to your Octarin workspace.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"octarin",
|