pairling 0.2.4 → 0.2.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pairling",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Pair your iPhone with the AI coding agents running on your Mac. CLI and local runtime installer for the Pairling iOS app.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pairling",
|
|
@@ -15,10 +15,6 @@
|
|
|
15
15
|
"bugs": {
|
|
16
16
|
"url": "https://pairling.dev/support"
|
|
17
17
|
},
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "https://github.com/mergimg0/pairling-helper"
|
|
21
|
-
},
|
|
22
18
|
"license": "UNLICENSED",
|
|
23
19
|
"author": "Pairling (https://pairling.dev)",
|
|
24
20
|
"type": "module",
|
|
@@ -39,8 +35,12 @@
|
|
|
39
35
|
"publishConfig": {
|
|
40
36
|
"access": "public"
|
|
41
37
|
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/mergimg0/pairling-helper"
|
|
41
|
+
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
|
-
"@pairling/runtime-darwin-arm64": "0.2.
|
|
44
|
-
"@pairling/runtime-darwin-x64": "0.2.
|
|
43
|
+
"@pairling/runtime-darwin-arm64": "0.2.5",
|
|
44
|
+
"@pairling/runtime-darwin-x64": "0.2.5"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2f094bd
|
package/payload/mac/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.5
|
|
@@ -747,7 +747,20 @@ ptybroker_live_session_count() {
|
|
|
747
747
|
import json
|
|
748
748
|
import sys
|
|
749
749
|
|
|
750
|
-
|
|
750
|
+
def load_json_arg(raw):
|
|
751
|
+
text = str(raw or "").strip()
|
|
752
|
+
decoder = json.JSONDecoder()
|
|
753
|
+
for index, char in enumerate(text):
|
|
754
|
+
if char not in "{[":
|
|
755
|
+
continue
|
|
756
|
+
try:
|
|
757
|
+
value, _ = decoder.raw_decode(text[index:])
|
|
758
|
+
return value
|
|
759
|
+
except json.JSONDecodeError:
|
|
760
|
+
continue
|
|
761
|
+
return {}
|
|
762
|
+
|
|
763
|
+
payload = load_json_arg(sys.argv[1])
|
|
751
764
|
status = payload.get("status") if isinstance(payload.get("status"), dict) else {}
|
|
752
765
|
print(status.get("live_session_count", "unknown"))
|
|
753
766
|
PY
|
|
@@ -799,7 +812,20 @@ ptybroker_live_revision() {
|
|
|
799
812
|
import json
|
|
800
813
|
import sys
|
|
801
814
|
|
|
802
|
-
|
|
815
|
+
def load_json_arg(raw):
|
|
816
|
+
text = str(raw or "").strip()
|
|
817
|
+
decoder = json.JSONDecoder()
|
|
818
|
+
for index, char in enumerate(text):
|
|
819
|
+
if char not in "{[":
|
|
820
|
+
continue
|
|
821
|
+
try:
|
|
822
|
+
value, _ = decoder.raw_decode(text[index:])
|
|
823
|
+
return value
|
|
824
|
+
except json.JSONDecodeError:
|
|
825
|
+
continue
|
|
826
|
+
return {}
|
|
827
|
+
|
|
828
|
+
payload = load_json_arg(sys.argv[1])
|
|
803
829
|
status = payload.get("status") if isinstance(payload.get("status"), dict) else payload
|
|
804
830
|
print(status.get("source_revision") or "")
|
|
805
831
|
PY
|
|
@@ -813,7 +839,20 @@ import sys
|
|
|
813
839
|
from pathlib import Path
|
|
814
840
|
|
|
815
841
|
current = Path(sys.argv[1])
|
|
816
|
-
|
|
842
|
+
def load_json_arg(raw):
|
|
843
|
+
text = str(raw or "").strip()
|
|
844
|
+
decoder = json.JSONDecoder()
|
|
845
|
+
for index, char in enumerate(text):
|
|
846
|
+
if char not in "{[":
|
|
847
|
+
continue
|
|
848
|
+
try:
|
|
849
|
+
value, _ = decoder.raw_decode(text[index:])
|
|
850
|
+
return value
|
|
851
|
+
except json.JSONDecodeError:
|
|
852
|
+
continue
|
|
853
|
+
return {}
|
|
854
|
+
|
|
855
|
+
payload = load_json_arg(sys.argv[2])
|
|
817
856
|
live = payload.get("status") if isinstance(payload.get("status"), dict) else payload
|
|
818
857
|
|
|
819
858
|
def read_revision(root: Path):
|
|
@@ -881,7 +920,20 @@ ptybroker_report_deferred_restart() {
|
|
|
881
920
|
import json
|
|
882
921
|
import sys
|
|
883
922
|
|
|
884
|
-
|
|
923
|
+
def load_json_arg(raw):
|
|
924
|
+
text = str(raw or "").strip()
|
|
925
|
+
decoder = json.JSONDecoder()
|
|
926
|
+
for index, char in enumerate(text):
|
|
927
|
+
if char not in "{[":
|
|
928
|
+
continue
|
|
929
|
+
try:
|
|
930
|
+
value, _ = decoder.raw_decode(text[index:])
|
|
931
|
+
return value
|
|
932
|
+
except json.JSONDecodeError:
|
|
933
|
+
continue
|
|
934
|
+
return {}
|
|
935
|
+
|
|
936
|
+
state = load_json_arg(sys.argv[1])
|
|
885
937
|
if state.get("state") != "stale_deferred":
|
|
886
938
|
raise SystemExit(0)
|
|
887
939
|
live = state.get("live") if isinstance(state.get("live"), dict) else {}
|
|
@@ -902,7 +954,20 @@ ptybroker_state_field() {
|
|
|
902
954
|
import json
|
|
903
955
|
import sys
|
|
904
956
|
|
|
905
|
-
|
|
957
|
+
def load_json_arg(raw):
|
|
958
|
+
text = str(raw or "").strip()
|
|
959
|
+
decoder = json.JSONDecoder()
|
|
960
|
+
for index, char in enumerate(text):
|
|
961
|
+
if char not in "{[":
|
|
962
|
+
continue
|
|
963
|
+
try:
|
|
964
|
+
value, _ = decoder.raw_decode(text[index:])
|
|
965
|
+
return value
|
|
966
|
+
except json.JSONDecodeError:
|
|
967
|
+
continue
|
|
968
|
+
return {}
|
|
969
|
+
|
|
970
|
+
payload = load_json_arg(sys.argv[1])
|
|
906
971
|
value = payload
|
|
907
972
|
for part in sys.argv[2].split("."):
|
|
908
973
|
if isinstance(value, dict):
|
|
@@ -1089,7 +1154,7 @@ install_runtime() {
|
|
|
1089
1154
|
log "Installed Pairling runtime $RELEASE_NAME"
|
|
1090
1155
|
if ! is_dry_run; then
|
|
1091
1156
|
log ""
|
|
1092
|
-
if ! pair_runtime --qr; then
|
|
1157
|
+
if ! PAIRLING_CONNECTD_ROUTE_WAIT_SECONDS="${PAIRLING_CONNECTD_ROUTE_WAIT_SECONDS:-35}" pair_runtime --qr; then
|
|
1093
1158
|
log "Pairling installed, but setup could not generate a pairing invitation. Run: pairling doctor --json; pairling pair --qr" >&2
|
|
1094
1159
|
exit 1
|
|
1095
1160
|
fi
|
|
@@ -1156,6 +1221,7 @@ import os
|
|
|
1156
1221
|
import socket
|
|
1157
1222
|
import subprocess
|
|
1158
1223
|
import sys
|
|
1224
|
+
import time
|
|
1159
1225
|
import urllib.parse
|
|
1160
1226
|
import urllib.error
|
|
1161
1227
|
import urllib.request
|
|
@@ -1268,34 +1334,64 @@ def detected_tailnet_ip() -> str:
|
|
|
1268
1334
|
return ip
|
|
1269
1335
|
return ""
|
|
1270
1336
|
|
|
1337
|
+
def connectd_route_wait_seconds() -> float:
|
|
1338
|
+
try:
|
|
1339
|
+
return min(max(float(os.environ.get("PAIRLING_CONNECTD_ROUTE_WAIT_SECONDS") or "0"), 0.0), 60.0)
|
|
1340
|
+
except ValueError:
|
|
1341
|
+
return 0.0
|
|
1342
|
+
|
|
1343
|
+
def connectd_route_poll_seconds() -> float:
|
|
1344
|
+
try:
|
|
1345
|
+
return min(max(float(os.environ.get("PAIRLING_CONNECTD_ROUTE_POLL_SECONDS") or "0.5"), 0.1), 2.0)
|
|
1346
|
+
except ValueError:
|
|
1347
|
+
return 0.5
|
|
1348
|
+
|
|
1349
|
+
def status_could_be_ready_soon(status: dict) -> bool:
|
|
1350
|
+
if not status:
|
|
1351
|
+
return True
|
|
1352
|
+
if status.get("auth_url_present"):
|
|
1353
|
+
return False
|
|
1354
|
+
return True
|
|
1355
|
+
|
|
1356
|
+
def ready_connectd_route():
|
|
1357
|
+
wait_seconds = connectd_route_wait_seconds()
|
|
1358
|
+
poll_seconds = connectd_route_poll_seconds()
|
|
1359
|
+
deadline = time.monotonic() + wait_seconds
|
|
1360
|
+
while True:
|
|
1361
|
+
status = fetch_connectd_status(timeout_seconds=0.7)
|
|
1362
|
+
connect_routes = advertised_pairling_connect_routes(status)
|
|
1363
|
+
if connect_routes:
|
|
1364
|
+
return connect_routes[0]
|
|
1365
|
+
if wait_seconds <= 0 or time.monotonic() >= deadline or not status_could_be_ready_soon(status):
|
|
1366
|
+
return None
|
|
1367
|
+
time.sleep(min(poll_seconds, max(0.0, deadline - time.monotonic())))
|
|
1368
|
+
|
|
1271
1369
|
def default_pair_route(port_number: int) -> dict:
|
|
1272
1370
|
for key in ("PAIRLING_PAIR_BASE_URL", "PAIRLING_PUBLIC_BASE_URL"):
|
|
1273
1371
|
value = os.environ.get(key)
|
|
1274
1372
|
if value:
|
|
1275
1373
|
return {"base_url": value, "source": "explicit_override", "status": "override"}
|
|
1276
|
-
#
|
|
1277
|
-
#
|
|
1278
|
-
#
|
|
1279
|
-
#
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
if lan_ip:
|
|
1283
|
-
return {"base_url": f"http://{lan_ip}:{port_number}", "source": "lan", "status": "fallback", "kind": "lan"}
|
|
1284
|
-
if os.environ.get("PAIRLING_DISABLE_BONJOUR") != "1" and os.environ.get("PAIRLING_TEST_DISABLE_BONJOUR") != "1":
|
|
1285
|
-
return {"base_url": f"http://{socket.gethostname()}.local:{port_number}", "source": "bonjour", "status": "fallback", "kind": "bonjour"}
|
|
1286
|
-
connect_routes = advertised_pairling_connect_routes(fetch_connectd_status(timeout_seconds=0.7))
|
|
1287
|
-
if connect_routes:
|
|
1288
|
-
route = connect_routes[0]
|
|
1374
|
+
# Remote-first pairing: if connectd reports a ready Pairling Connect route,
|
|
1375
|
+
# the QR advertises that route and the iOS app claims it through the
|
|
1376
|
+
# embedded pre-pair transport. LAN/Bonjour are explicit degraded fallbacks
|
|
1377
|
+
# when Pairling Connect is not ready.
|
|
1378
|
+
route = ready_connectd_route()
|
|
1379
|
+
if route:
|
|
1289
1380
|
return {
|
|
1290
1381
|
"base_url": route["base_url"],
|
|
1291
1382
|
"source": route["source"],
|
|
1292
1383
|
"status": route["status"],
|
|
1293
1384
|
"kind": route["kind"],
|
|
1294
1385
|
}
|
|
1386
|
+
lan_ip = detected_lan_ip()
|
|
1387
|
+
if lan_ip:
|
|
1388
|
+
return {"base_url": f"http://{lan_ip}:{port_number}", "source": "lan", "status": "fallback", "kind": "lan"}
|
|
1389
|
+
if os.environ.get("PAIRLING_DISABLE_BONJOUR") != "1" and os.environ.get("PAIRLING_TEST_DISABLE_BONJOUR") != "1":
|
|
1390
|
+
return {"base_url": f"http://{socket.gethostname()}.local:{port_number}", "source": "bonjour", "status": "fallback", "kind": "bonjour"}
|
|
1295
1391
|
tailnet_ip = detected_tailnet_ip()
|
|
1296
1392
|
if tailnet_ip:
|
|
1297
|
-
return {"base_url": f"http://{tailnet_ip}:{port_number}", "source": "standalone_tailnet", "status": "fallback"}
|
|
1298
|
-
return {"base_url": f"http://{socket.gethostname()}.local:{port_number}", "source": "bonjour", "status": "fallback"}
|
|
1393
|
+
return {"base_url": f"http://{tailnet_ip}:{port_number}", "source": "standalone_tailnet", "status": "fallback", "kind": "standalone_tailnet"}
|
|
1394
|
+
return {"base_url": f"http://{socket.gethostname()}.local:{port_number}", "source": "bonjour", "status": "fallback", "kind": "bonjour"}
|
|
1299
1395
|
|
|
1300
1396
|
pair_route = default_pair_route(int(port))
|
|
1301
1397
|
base_url = str(pair_route.get("base_url") or "")
|
|
@@ -1316,6 +1412,11 @@ if pair_id and secret:
|
|
|
1316
1412
|
pair_params["route_status"] = "ready"
|
|
1317
1413
|
pair_params["route_kind"] = str(pair_route.get("kind") or "tailnet")
|
|
1318
1414
|
pair_params["route_contract"] = "pairling-runtime-v1"
|
|
1415
|
+
elif pair_route.get("status") == "fallback":
|
|
1416
|
+
pair_params["route_source"] = "local_fallback"
|
|
1417
|
+
pair_params["route_status"] = "degraded"
|
|
1418
|
+
pair_params["route_kind"] = str(pair_route.get("kind") or pair_route.get("source") or "local")
|
|
1419
|
+
pair_params["route_contract"] = "pairling-runtime-v1"
|
|
1319
1420
|
manual = {
|
|
1320
1421
|
"base_url": base_url,
|
|
1321
1422
|
"pair_id": pair_id,
|
package/payload-manifest.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"connectd": {
|
|
3
3
|
"darwin-arm64": {
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "96400014bc7d32bd1b976216eb6dc76a6621d9da34a2ef02da87c72957dea170",
|
|
5
5
|
"team_id": "965AVD34A3"
|
|
6
6
|
},
|
|
7
7
|
"darwin-x64": {
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "bb4df01a72f6c2032c8d7341f5d243fa3e6c99295c033dbf3f99a7d7eed650b7",
|
|
9
9
|
"team_id": "965AVD34A3"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "payload/mac/SOURCE_REVISION",
|
|
23
|
-
"sha256": "
|
|
23
|
+
"sha256": "6fba526ba8b27c395d71b9d6067bb09fe9458c5d650a92af80541ad7971a3ba3"
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"path": "payload/mac/VERSION",
|
|
27
|
-
"sha256": "
|
|
27
|
+
"sha256": "e959f750e92dbb7614ae28ac96f0a37272caed81d8bbb758791af7bfbb6106a0"
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"path": "payload/mac/companiond/app_attest_lan.py",
|
|
@@ -252,7 +252,7 @@
|
|
|
252
252
|
},
|
|
253
253
|
{
|
|
254
254
|
"path": "payload/mac/install/install-runtime.sh",
|
|
255
|
-
"sha256": "
|
|
255
|
+
"sha256": "87c1db89b281b78d0809b4360c54c5c57b8e4f25a473496373315236ffeb6117"
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
258
|
"path": "payload/mac/install/psk_dependency_check.py",
|
|
@@ -276,8 +276,8 @@
|
|
|
276
276
|
}
|
|
277
277
|
],
|
|
278
278
|
"package": "pairling",
|
|
279
|
-
"package_version": "0.2.
|
|
279
|
+
"package_version": "0.2.5",
|
|
280
280
|
"schema_version": 1,
|
|
281
281
|
"source_dirty": false,
|
|
282
|
-
"source_revision": "
|
|
282
|
+
"source_revision": "2f094bd"
|
|
283
283
|
}
|