-3
.gitmodules
-3
.gitmodules
+622
-72
cli/Cargo.lock
+622
-72
cli/Cargo.lock
···
139
140
[[package]]
141
name = "async-compression"
142
-
version = "0.4.32"
143
source = "registry+https://github.com/rust-lang/crates.io-index"
144
-
checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0"
145
dependencies = [
146
"compression-codecs",
147
"compression-core",
···
158
dependencies = [
159
"proc-macro2",
160
"quote",
161
-
"syn 2.0.108",
162
]
163
164
[[package]]
···
174
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
175
176
[[package]]
177
name = "backtrace"
178
version = "0.3.76"
179
source = "registry+https://github.com/rust-lang/crates.io-index"
···
274
"proc-macro2",
275
"quote",
276
"rustversion",
277
-
"syn 2.0.108",
278
]
279
280
[[package]]
···
348
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
349
350
[[package]]
351
name = "bytes"
352
version = "1.10.1"
353
source = "registry+https://github.com/rust-lang/crates.io-index"
···
367
368
[[package]]
369
name = "cc"
370
-
version = "1.2.44"
371
source = "registry+https://github.com/rust-lang/crates.io-index"
372
-
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
373
dependencies = [
374
"find-msvc-tools",
375
"shlex",
···
494
"heck 0.5.0",
495
"proc-macro2",
496
"quote",
497
-
"syn 2.0.108",
498
]
499
500
[[package]]
···
521
522
[[package]]
523
name = "compression-codecs"
524
-
version = "0.4.31"
525
source = "registry+https://github.com/rust-lang/crates.io-index"
526
-
checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23"
527
dependencies = [
528
"compression-core",
529
"flate2",
···
532
533
[[package]]
534
name = "compression-core"
535
-
version = "0.4.29"
536
source = "registry+https://github.com/rust-lang/crates.io-index"
537
-
checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb"
538
539
[[package]]
540
name = "const-oid"
···
549
checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3"
550
551
[[package]]
552
name = "core-foundation"
553
version = "0.9.4"
554
source = "registry+https://github.com/rust-lang/crates.io-index"
···
665
"proc-macro2",
666
"quote",
667
"strsim",
668
-
"syn 2.0.108",
669
]
670
671
[[package]]
···
676
dependencies = [
677
"darling_core",
678
"quote",
679
-
"syn 2.0.108",
680
]
681
682
[[package]]
···
716
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
717
dependencies = [
718
"data-encoding",
719
-
"syn 2.0.108",
720
]
721
722
[[package]]
···
751
]
752
753
[[package]]
754
name = "digest"
755
version = "0.10.7"
756
source = "registry+https://github.com/rust-lang/crates.io-index"
···
791
dependencies = [
792
"proc-macro2",
793
"quote",
794
-
"syn 2.0.108",
795
]
796
797
[[package]]
···
852
"heck 0.5.0",
853
"proc-macro2",
854
"quote",
855
-
"syn 2.0.108",
856
]
857
858
[[package]]
···
956
]
957
958
[[package]]
959
name = "futures-channel"
960
version = "0.3.31"
961
source = "registry+https://github.com/rust-lang/crates.io-index"
···
989
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
990
991
[[package]]
992
name = "futures-macro"
993
version = "0.3.31"
994
source = "registry+https://github.com/rust-lang/crates.io-index"
···
996
dependencies = [
997
"proc-macro2",
998
"quote",
999
-
"syn 2.0.108",
1000
]
1001
1002
[[package]]
···
1027
"pin-project-lite",
1028
"pin-utils",
1029
"slab",
1030
]
1031
1032
[[package]]
···
1236
"markup5ever",
1237
"proc-macro2",
1238
"quote",
1239
-
"syn 2.0.108",
1240
]
1241
1242
[[package]]
···
1274
]
1275
1276
[[package]]
1277
name = "httparse"
1278
version = "1.10.1"
1279
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1287
1288
[[package]]
1289
name = "hyper"
1290
-
version = "1.7.0"
1291
source = "registry+https://github.com/rust-lang/crates.io-index"
1292
-
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
1293
dependencies = [
1294
"atomic-waker",
1295
"bytes",
···
1299
"http",
1300
"http-body",
1301
"httparse",
1302
"itoa",
1303
"pin-project-lite",
1304
"pin-utils",
···
1362
"js-sys",
1363
"log",
1364
"wasm-bindgen",
1365
-
"windows-core",
1366
]
1367
1368
[[package]]
···
1554
1555
[[package]]
1556
name = "iri-string"
1557
-
version = "0.7.8"
1558
source = "registry+https://github.com/rust-lang/crates.io-index"
1559
-
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
1560
dependencies = [
1561
"memchr",
1562
"serde",
···
1583
[[package]]
1584
name = "jacquard"
1585
version = "0.9.0"
1586
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1587
dependencies = [
1588
"bytes",
1589
"getrandom 0.2.16",
···
1611
[[package]]
1612
name = "jacquard-api"
1613
version = "0.9.0"
1614
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1615
dependencies = [
1616
"bon",
1617
"bytes",
···
1629
[[package]]
1630
name = "jacquard-common"
1631
version = "0.9.0"
1632
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1633
dependencies = [
1634
"base64 0.22.1",
1635
"bon",
1636
"bytes",
1637
"chrono",
1638
"cid",
1639
"getrandom 0.2.16",
1640
"getrandom 0.3.4",
1641
"http",
···
1645
"miette",
1646
"multibase",
1647
"multihash",
1648
"ouroboros",
1649
"p256",
1650
"rand 0.9.2",
···
1658
"smol_str",
1659
"thiserror 2.0.17",
1660
"tokio",
1661
"tokio-util",
1662
"trait-variant",
1663
"url",
···
1666
[[package]]
1667
name = "jacquard-derive"
1668
version = "0.9.0"
1669
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1670
dependencies = [
1671
"heck 0.5.0",
1672
"jacquard-lexicon",
1673
"proc-macro2",
1674
"quote",
1675
-
"syn 2.0.108",
1676
]
1677
1678
[[package]]
1679
name = "jacquard-identity"
1680
version = "0.9.1"
1681
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1682
dependencies = [
1683
"bon",
1684
"bytes",
···
1704
[[package]]
1705
name = "jacquard-lexicon"
1706
version = "0.9.1"
1707
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1708
dependencies = [
1709
"cid",
1710
"dashmap",
···
1722
"serde_repr",
1723
"serde_with",
1724
"sha2",
1725
-
"syn 2.0.108",
1726
"thiserror 2.0.17",
1727
"unicode-segmentation",
1728
]
···
1730
[[package]]
1731
name = "jacquard-oauth"
1732
version = "0.9.0"
1733
-
source = "git+https://tangled.org/@nonbinary.computer/jacquard#b5cc9b35e38e24e1890ae55e700dcfad0d6d433a"
1734
dependencies = [
1735
"base64 0.22.1",
1736
"bytes",
···
1856
source = "registry+https://github.com/rust-lang/crates.io-index"
1857
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
1858
dependencies = [
1859
-
"spin",
1860
]
1861
1862
[[package]]
···
1916
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
1917
1918
[[package]]
1919
name = "lru-cache"
1920
version = "0.1.2"
1921
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1974
]
1975
1976
[[package]]
1977
name = "memchr"
1978
version = "2.7.6"
1979
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2006
dependencies = [
2007
"proc-macro2",
2008
"quote",
2009
-
"syn 2.0.108",
2010
]
2011
2012
[[package]]
···
2108
]
2109
2110
[[package]]
2111
name = "ndk-context"
2112
version = "0.1.1"
2113
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2130
]
2131
2132
[[package]]
2133
name = "num-bigint-dig"
2134
-
version = "0.8.5"
2135
source = "registry+https://github.com/rust-lang/crates.io-index"
2136
-
checksum = "82c79c15c05d4bf82b6f5ef163104cc81a760d8e874d38ac50ab67c8877b647b"
2137
dependencies = [
2138
"lazy_static",
2139
"libm",
···
2247
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
2248
2249
[[package]]
2250
name = "option-ext"
2251
version = "0.2.0"
2252
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2273
"proc-macro2",
2274
"proc-macro2-diagnostics",
2275
"quote",
2276
-
"syn 2.0.108",
2277
]
2278
2279
[[package]]
···
2303
"elliptic-curve",
2304
"primeorder",
2305
]
2306
2307
[[package]]
2308
name = "parking_lot"
···
2381
]
2382
2383
[[package]]
2384
name = "pin-project-lite"
2385
version = "0.2.16"
2386
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2450
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
2451
dependencies = [
2452
"proc-macro2",
2453
-
"syn 2.0.108",
2454
]
2455
2456
[[package]]
···
2503
dependencies = [
2504
"proc-macro2",
2505
"quote",
2506
-
"syn 2.0.108",
2507
"version_check",
2508
"yansi",
2509
]
···
2571
2572
[[package]]
2573
name = "quote"
2574
-
version = "1.0.41"
2575
source = "registry+https://github.com/rust-lang/crates.io-index"
2576
-
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
2577
dependencies = [
2578
"proc-macro2",
2579
]
···
2686
dependencies = [
2687
"proc-macro2",
2688
"quote",
2689
-
"syn 2.0.108",
2690
]
2691
2692
[[package]]
···
2752
"tokio",
2753
"tokio-rustls",
2754
"tokio-util",
2755
-
"tower",
2756
-
"tower-http",
2757
"tower-service",
2758
"url",
2759
"wasm-bindgen",
···
2864
2865
[[package]]
2866
name = "rustls"
2867
-
version = "0.23.34"
2868
source = "registry+https://github.com/rust-lang/crates.io-index"
2869
-
checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7"
2870
dependencies = [
2871
"once_cell",
2872
"ring",
···
2874
"rustls-webpki",
2875
"subtle",
2876
"zeroize",
2877
]
2878
2879
[[package]]
···
2925
]
2926
2927
[[package]]
2928
name = "schemars"
2929
version = "0.9.0"
2930
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2938
2939
[[package]]
2940
name = "schemars"
2941
-
version = "1.0.4"
2942
source = "registry+https://github.com/rust-lang/crates.io-index"
2943
-
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
2944
dependencies = [
2945
"dyn-clone",
2946
"ref-cast",
···
2949
]
2950
2951
[[package]]
2952
name = "scopeguard"
2953
version = "1.2.0"
2954
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2967
"subtle",
2968
"zeroize",
2969
]
2970
2971
[[package]]
2972
name = "serde"
···
3005
dependencies = [
3006
"proc-macro2",
3007
"quote",
3008
-
"syn 2.0.108",
3009
]
3010
3011
[[package]]
···
3047
]
3048
3049
[[package]]
3050
name = "serde_repr"
3051
version = "0.1.20"
3052
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3054
dependencies = [
3055
"proc-macro2",
3056
"quote",
3057
-
"syn 2.0.108",
3058
]
3059
3060
[[package]]
···
3081
"indexmap 1.9.3",
3082
"indexmap 2.12.0",
3083
"schemars 0.9.0",
3084
-
"schemars 1.0.4",
3085
"serde_core",
3086
"serde_json",
3087
"serde_with_macros",
···
3097
"darling",
3098
"proc-macro2",
3099
"quote",
3100
-
"syn 2.0.108",
3101
]
3102
3103
[[package]]
···
3115
"cfg-if",
3116
"cpufeatures",
3117
"digest",
3118
]
3119
3120
[[package]]
···
3212
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
3213
3214
[[package]]
3215
name = "spki"
3216
version = "0.7.3"
3217
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3243
"quote",
3244
"serde",
3245
"sha2",
3246
-
"syn 2.0.108",
3247
"thiserror 1.0.69",
3248
]
3249
···
3324
3325
[[package]]
3326
name = "syn"
3327
-
version = "2.0.108"
3328
source = "registry+https://github.com/rust-lang/crates.io-index"
3329
-
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
3330
dependencies = [
3331
"proc-macro2",
3332
"quote",
···
3350
dependencies = [
3351
"proc-macro2",
3352
"quote",
3353
-
"syn 2.0.108",
3354
]
3355
3356
[[package]]
···
3450
dependencies = [
3451
"proc-macro2",
3452
"quote",
3453
-
"syn 2.0.108",
3454
]
3455
3456
[[package]]
···
3461
dependencies = [
3462
"proc-macro2",
3463
"quote",
3464
-
"syn 2.0.108",
3465
]
3466
3467
[[package]]
···
3568
dependencies = [
3569
"proc-macro2",
3570
"quote",
3571
-
"syn 2.0.108",
3572
]
3573
3574
[[package]]
···
3582
]
3583
3584
[[package]]
3585
name = "tokio-util"
3586
-
version = "0.7.16"
3587
source = "registry+https://github.com/rust-lang/crates.io-index"
3588
-
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
3589
dependencies = [
3590
"bytes",
3591
"futures-core",
3592
"futures-sink",
3593
"pin-project-lite",
3594
"tokio",
3595
]
3596
3597
[[package]]
3598
name = "tower"
3599
version = "0.5.2"
3600
source = "registry+https://github.com/rust-lang/crates.io-index"
3601
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
···
3607
"tokio",
3608
"tower-layer",
3609
"tower-service",
3610
]
3611
3612
[[package]]
···
3622
"http-body",
3623
"iri-string",
3624
"pin-project-lite",
3625
-
"tower",
3626
"tower-layer",
3627
"tower-service",
3628
]
···
3645
source = "registry+https://github.com/rust-lang/crates.io-index"
3646
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
3647
dependencies = [
3648
"pin-project-lite",
3649
"tracing-attributes",
3650
"tracing-core",
···
3658
dependencies = [
3659
"proc-macro2",
3660
"quote",
3661
-
"syn 2.0.108",
3662
]
3663
3664
[[package]]
···
3668
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
3669
dependencies = [
3670
"once_cell",
3671
]
3672
3673
[[package]]
···
3678
dependencies = [
3679
"proc-macro2",
3680
"quote",
3681
-
"syn 2.0.108",
3682
]
3683
3684
[[package]]
···
3694
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
3695
3696
[[package]]
3697
name = "twoway"
3698
version = "0.1.8"
3699
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3745
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
3746
3747
[[package]]
3748
name = "unsigned-varint"
3749
version = "0.8.0"
3750
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3791
version = "0.2.2"
3792
source = "registry+https://github.com/rust-lang/crates.io-index"
3793
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
3794
3795
[[package]]
3796
name = "version_check"
···
3877
"bumpalo",
3878
"proc-macro2",
3879
"quote",
3880
-
"syn 2.0.108",
3881
"wasm-bindgen-shared",
3882
]
3883
···
3976
]
3977
3978
[[package]]
3979
name = "windows-core"
3980
version = "0.62.2"
3981
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3989
]
3990
3991
[[package]]
3992
name = "windows-implement"
3993
version = "0.60.2"
3994
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3996
dependencies = [
3997
"proc-macro2",
3998
"quote",
3999
-
"syn 2.0.108",
4000
]
4001
4002
[[package]]
···
4007
dependencies = [
4008
"proc-macro2",
4009
"quote",
4010
-
"syn 2.0.108",
4011
]
4012
4013
[[package]]
···
4021
version = "0.2.1"
4022
source = "registry+https://github.com/rust-lang/crates.io-index"
4023
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
4024
4025
[[package]]
4026
name = "windows-registry"
···
4178
]
4179
4180
[[package]]
4181
name = "windows_aarch64_gnullvm"
4182
version = "0.42.2"
4183
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4369
4370
[[package]]
4371
name = "wisp-cli"
4372
-
version = "0.1.0"
4373
dependencies = [
4374
"base64 0.22.1",
4375
"bytes",
4376
"clap",
4377
"flate2",
4378
"futures",
···
4387
"mime_guess",
4388
"multibase",
4389
"multihash",
4390
"reqwest",
4391
"rustversion",
4392
"serde",
···
4394
"sha2",
4395
"shellexpand",
4396
"tokio",
4397
"walkdir",
4398
]
4399
···
4445
dependencies = [
4446
"proc-macro2",
4447
"quote",
4448
-
"syn 2.0.108",
4449
"synstructure",
4450
]
4451
···
4466
dependencies = [
4467
"proc-macro2",
4468
"quote",
4469
-
"syn 2.0.108",
4470
]
4471
4472
[[package]]
···
4486
dependencies = [
4487
"proc-macro2",
4488
"quote",
4489
-
"syn 2.0.108",
4490
"synstructure",
4491
]
4492
···
4529
dependencies = [
4530
"proc-macro2",
4531
"quote",
4532
-
"syn 2.0.108",
4533
]
···
139
140
[[package]]
141
name = "async-compression"
142
+
version = "0.4.33"
143
source = "registry+https://github.com/rust-lang/crates.io-index"
144
+
checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2"
145
dependencies = [
146
"compression-codecs",
147
"compression-core",
···
158
dependencies = [
159
"proc-macro2",
160
"quote",
161
+
"syn 2.0.110",
162
]
163
164
[[package]]
···
174
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
175
176
[[package]]
177
+
name = "axum"
178
+
version = "0.7.9"
179
+
source = "registry+https://github.com/rust-lang/crates.io-index"
180
+
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
181
+
dependencies = [
182
+
"async-trait",
183
+
"axum-core",
184
+
"bytes",
185
+
"futures-util",
186
+
"http",
187
+
"http-body",
188
+
"http-body-util",
189
+
"hyper",
190
+
"hyper-util",
191
+
"itoa",
192
+
"matchit",
193
+
"memchr",
194
+
"mime",
195
+
"percent-encoding",
196
+
"pin-project-lite",
197
+
"rustversion",
198
+
"serde",
199
+
"serde_json",
200
+
"serde_path_to_error",
201
+
"serde_urlencoded",
202
+
"sync_wrapper",
203
+
"tokio",
204
+
"tower 0.5.2",
205
+
"tower-layer",
206
+
"tower-service",
207
+
"tracing",
208
+
]
209
+
210
+
[[package]]
211
+
name = "axum-core"
212
+
version = "0.4.5"
213
+
source = "registry+https://github.com/rust-lang/crates.io-index"
214
+
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
215
+
dependencies = [
216
+
"async-trait",
217
+
"bytes",
218
+
"futures-util",
219
+
"http",
220
+
"http-body",
221
+
"http-body-util",
222
+
"mime",
223
+
"pin-project-lite",
224
+
"rustversion",
225
+
"sync_wrapper",
226
+
"tower-layer",
227
+
"tower-service",
228
+
"tracing",
229
+
]
230
+
231
+
[[package]]
232
name = "backtrace"
233
version = "0.3.76"
234
source = "registry+https://github.com/rust-lang/crates.io-index"
···
329
"proc-macro2",
330
"quote",
331
"rustversion",
332
+
"syn 2.0.110",
333
]
334
335
[[package]]
···
403
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
404
405
[[package]]
406
+
name = "byteorder"
407
+
version = "1.5.0"
408
+
source = "registry+https://github.com/rust-lang/crates.io-index"
409
+
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
410
+
411
+
[[package]]
412
name = "bytes"
413
version = "1.10.1"
414
source = "registry+https://github.com/rust-lang/crates.io-index"
···
428
429
[[package]]
430
name = "cc"
431
+
version = "1.2.45"
432
source = "registry+https://github.com/rust-lang/crates.io-index"
433
+
checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
434
dependencies = [
435
"find-msvc-tools",
436
"shlex",
···
555
"heck 0.5.0",
556
"proc-macro2",
557
"quote",
558
+
"syn 2.0.110",
559
]
560
561
[[package]]
···
582
583
[[package]]
584
name = "compression-codecs"
585
+
version = "0.4.32"
586
source = "registry+https://github.com/rust-lang/crates.io-index"
587
+
checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b"
588
dependencies = [
589
"compression-core",
590
"flate2",
···
593
594
[[package]]
595
name = "compression-core"
596
+
version = "0.4.30"
597
source = "registry+https://github.com/rust-lang/crates.io-index"
598
+
checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582"
599
600
[[package]]
601
name = "const-oid"
···
610
checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3"
611
612
[[package]]
613
+
name = "cordyceps"
614
+
version = "0.3.4"
615
+
source = "registry+https://github.com/rust-lang/crates.io-index"
616
+
checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a"
617
+
dependencies = [
618
+
"loom",
619
+
"tracing",
620
+
]
621
+
622
+
[[package]]
623
name = "core-foundation"
624
version = "0.9.4"
625
source = "registry+https://github.com/rust-lang/crates.io-index"
···
736
"proc-macro2",
737
"quote",
738
"strsim",
739
+
"syn 2.0.110",
740
]
741
742
[[package]]
···
747
dependencies = [
748
"darling_core",
749
"quote",
750
+
"syn 2.0.110",
751
]
752
753
[[package]]
···
787
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
788
dependencies = [
789
"data-encoding",
790
+
"syn 2.0.110",
791
]
792
793
[[package]]
···
822
]
823
824
[[package]]
825
+
name = "derive_more"
826
+
version = "1.0.0"
827
+
source = "registry+https://github.com/rust-lang/crates.io-index"
828
+
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
829
+
dependencies = [
830
+
"derive_more-impl",
831
+
]
832
+
833
+
[[package]]
834
+
name = "derive_more-impl"
835
+
version = "1.0.0"
836
+
source = "registry+https://github.com/rust-lang/crates.io-index"
837
+
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
838
+
dependencies = [
839
+
"proc-macro2",
840
+
"quote",
841
+
"syn 2.0.110",
842
+
"unicode-xid",
843
+
]
844
+
845
+
[[package]]
846
+
name = "diatomic-waker"
847
+
version = "0.2.3"
848
+
source = "registry+https://github.com/rust-lang/crates.io-index"
849
+
checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c"
850
+
851
+
[[package]]
852
name = "digest"
853
version = "0.10.7"
854
source = "registry+https://github.com/rust-lang/crates.io-index"
···
889
dependencies = [
890
"proc-macro2",
891
"quote",
892
+
"syn 2.0.110",
893
]
894
895
[[package]]
···
950
"heck 0.5.0",
951
"proc-macro2",
952
"quote",
953
+
"syn 2.0.110",
954
]
955
956
[[package]]
···
1054
]
1055
1056
[[package]]
1057
+
name = "futures-buffered"
1058
+
version = "0.2.12"
1059
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1060
+
checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd"
1061
+
dependencies = [
1062
+
"cordyceps",
1063
+
"diatomic-waker",
1064
+
"futures-core",
1065
+
"pin-project-lite",
1066
+
"spin 0.10.0",
1067
+
]
1068
+
1069
+
[[package]]
1070
name = "futures-channel"
1071
version = "0.3.31"
1072
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1100
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
1101
1102
[[package]]
1103
+
name = "futures-lite"
1104
+
version = "2.6.1"
1105
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1106
+
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
1107
+
dependencies = [
1108
+
"fastrand",
1109
+
"futures-core",
1110
+
"futures-io",
1111
+
"parking",
1112
+
"pin-project-lite",
1113
+
]
1114
+
1115
+
[[package]]
1116
name = "futures-macro"
1117
version = "0.3.31"
1118
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1120
dependencies = [
1121
"proc-macro2",
1122
"quote",
1123
+
"syn 2.0.110",
1124
]
1125
1126
[[package]]
···
1151
"pin-project-lite",
1152
"pin-utils",
1153
"slab",
1154
+
]
1155
+
1156
+
[[package]]
1157
+
name = "generator"
1158
+
version = "0.8.7"
1159
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1160
+
checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2"
1161
+
dependencies = [
1162
+
"cc",
1163
+
"cfg-if",
1164
+
"libc",
1165
+
"log",
1166
+
"rustversion",
1167
+
"windows",
1168
]
1169
1170
[[package]]
···
1374
"markup5ever",
1375
"proc-macro2",
1376
"quote",
1377
+
"syn 2.0.110",
1378
]
1379
1380
[[package]]
···
1412
]
1413
1414
[[package]]
1415
+
name = "http-range-header"
1416
+
version = "0.4.2"
1417
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1418
+
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
1419
+
1420
+
[[package]]
1421
name = "httparse"
1422
version = "1.10.1"
1423
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1431
1432
[[package]]
1433
name = "hyper"
1434
+
version = "1.8.0"
1435
source = "registry+https://github.com/rust-lang/crates.io-index"
1436
+
checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f"
1437
dependencies = [
1438
"atomic-waker",
1439
"bytes",
···
1443
"http",
1444
"http-body",
1445
"httparse",
1446
+
"httpdate",
1447
"itoa",
1448
"pin-project-lite",
1449
"pin-utils",
···
1507
"js-sys",
1508
"log",
1509
"wasm-bindgen",
1510
+
"windows-core 0.62.2",
1511
]
1512
1513
[[package]]
···
1699
1700
[[package]]
1701
name = "iri-string"
1702
+
version = "0.7.9"
1703
source = "registry+https://github.com/rust-lang/crates.io-index"
1704
+
checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
1705
dependencies = [
1706
"memchr",
1707
"serde",
···
1728
[[package]]
1729
name = "jacquard"
1730
version = "0.9.0"
1731
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1732
dependencies = [
1733
"bytes",
1734
"getrandom 0.2.16",
···
1756
[[package]]
1757
name = "jacquard-api"
1758
version = "0.9.0"
1759
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1760
dependencies = [
1761
"bon",
1762
"bytes",
···
1774
[[package]]
1775
name = "jacquard-common"
1776
version = "0.9.0"
1777
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1778
dependencies = [
1779
"base64 0.22.1",
1780
"bon",
1781
"bytes",
1782
"chrono",
1783
+
"ciborium",
1784
"cid",
1785
+
"futures",
1786
"getrandom 0.2.16",
1787
"getrandom 0.3.4",
1788
"http",
···
1792
"miette",
1793
"multibase",
1794
"multihash",
1795
+
"n0-future",
1796
"ouroboros",
1797
"p256",
1798
"rand 0.9.2",
···
1806
"smol_str",
1807
"thiserror 2.0.17",
1808
"tokio",
1809
+
"tokio-tungstenite-wasm",
1810
"tokio-util",
1811
"trait-variant",
1812
"url",
···
1815
[[package]]
1816
name = "jacquard-derive"
1817
version = "0.9.0"
1818
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1819
dependencies = [
1820
"heck 0.5.0",
1821
"jacquard-lexicon",
1822
"proc-macro2",
1823
"quote",
1824
+
"syn 2.0.110",
1825
]
1826
1827
[[package]]
1828
name = "jacquard-identity"
1829
version = "0.9.1"
1830
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1831
dependencies = [
1832
"bon",
1833
"bytes",
···
1853
[[package]]
1854
name = "jacquard-lexicon"
1855
version = "0.9.1"
1856
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1857
dependencies = [
1858
"cid",
1859
"dashmap",
···
1871
"serde_repr",
1872
"serde_with",
1873
"sha2",
1874
+
"syn 2.0.110",
1875
"thiserror 2.0.17",
1876
"unicode-segmentation",
1877
]
···
1879
[[package]]
1880
name = "jacquard-oauth"
1881
version = "0.9.0"
1882
+
source = "git+https://tangled.org/@nonbinary.computer/jacquard#5c79bb76de544cbd4fa8d5d8b01ba6e828f8ba65"
1883
dependencies = [
1884
"base64 0.22.1",
1885
"bytes",
···
2005
source = "registry+https://github.com/rust-lang/crates.io-index"
2006
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
2007
dependencies = [
2008
+
"spin 0.9.8",
2009
]
2010
2011
[[package]]
···
2065
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
2066
2067
[[package]]
2068
+
name = "loom"
2069
+
version = "0.7.2"
2070
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2071
+
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
2072
+
dependencies = [
2073
+
"cfg-if",
2074
+
"generator",
2075
+
"scoped-tls",
2076
+
"tracing",
2077
+
"tracing-subscriber",
2078
+
]
2079
+
2080
+
[[package]]
2081
name = "lru-cache"
2082
version = "0.1.2"
2083
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2136
]
2137
2138
[[package]]
2139
+
name = "matchers"
2140
+
version = "0.2.0"
2141
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2142
+
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
2143
+
dependencies = [
2144
+
"regex-automata",
2145
+
]
2146
+
2147
+
[[package]]
2148
+
name = "matchit"
2149
+
version = "0.7.3"
2150
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2151
+
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
2152
+
2153
+
[[package]]
2154
name = "memchr"
2155
version = "2.7.6"
2156
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2183
dependencies = [
2184
"proc-macro2",
2185
"quote",
2186
+
"syn 2.0.110",
2187
]
2188
2189
[[package]]
···
2285
]
2286
2287
[[package]]
2288
+
name = "n0-future"
2289
+
version = "0.1.3"
2290
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2291
+
checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794"
2292
+
dependencies = [
2293
+
"cfg_aliases",
2294
+
"derive_more",
2295
+
"futures-buffered",
2296
+
"futures-lite",
2297
+
"futures-util",
2298
+
"js-sys",
2299
+
"pin-project",
2300
+
"send_wrapper",
2301
+
"tokio",
2302
+
"tokio-util",
2303
+
"wasm-bindgen",
2304
+
"wasm-bindgen-futures",
2305
+
"web-time",
2306
+
]
2307
+
2308
+
[[package]]
2309
name = "ndk-context"
2310
version = "0.1.1"
2311
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2328
]
2329
2330
[[package]]
2331
+
name = "nu-ansi-term"
2332
+
version = "0.50.3"
2333
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2334
+
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
2335
+
dependencies = [
2336
+
"windows-sys 0.61.2",
2337
+
]
2338
+
2339
+
[[package]]
2340
name = "num-bigint-dig"
2341
+
version = "0.8.6"
2342
source = "registry+https://github.com/rust-lang/crates.io-index"
2343
+
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
2344
dependencies = [
2345
"lazy_static",
2346
"libm",
···
2454
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
2455
2456
[[package]]
2457
+
name = "openssl-probe"
2458
+
version = "0.1.6"
2459
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2460
+
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
2461
+
2462
+
[[package]]
2463
name = "option-ext"
2464
version = "0.2.0"
2465
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2486
"proc-macro2",
2487
"proc-macro2-diagnostics",
2488
"quote",
2489
+
"syn 2.0.110",
2490
]
2491
2492
[[package]]
···
2516
"elliptic-curve",
2517
"primeorder",
2518
]
2519
+
2520
+
[[package]]
2521
+
name = "parking"
2522
+
version = "2.2.1"
2523
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2524
+
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
2525
2526
[[package]]
2527
name = "parking_lot"
···
2600
]
2601
2602
[[package]]
2603
+
name = "pin-project"
2604
+
version = "1.1.10"
2605
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2606
+
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
2607
+
dependencies = [
2608
+
"pin-project-internal",
2609
+
]
2610
+
2611
+
[[package]]
2612
+
name = "pin-project-internal"
2613
+
version = "1.1.10"
2614
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2615
+
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
2616
+
dependencies = [
2617
+
"proc-macro2",
2618
+
"quote",
2619
+
"syn 2.0.110",
2620
+
]
2621
+
2622
+
[[package]]
2623
name = "pin-project-lite"
2624
version = "0.2.16"
2625
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2689
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
2690
dependencies = [
2691
"proc-macro2",
2692
+
"syn 2.0.110",
2693
]
2694
2695
[[package]]
···
2742
dependencies = [
2743
"proc-macro2",
2744
"quote",
2745
+
"syn 2.0.110",
2746
"version_check",
2747
"yansi",
2748
]
···
2810
2811
[[package]]
2812
name = "quote"
2813
+
version = "1.0.42"
2814
source = "registry+https://github.com/rust-lang/crates.io-index"
2815
+
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
2816
dependencies = [
2817
"proc-macro2",
2818
]
···
2925
dependencies = [
2926
"proc-macro2",
2927
"quote",
2928
+
"syn 2.0.110",
2929
]
2930
2931
[[package]]
···
2991
"tokio",
2992
"tokio-rustls",
2993
"tokio-util",
2994
+
"tower 0.5.2",
2995
+
"tower-http 0.6.6",
2996
"tower-service",
2997
"url",
2998
"wasm-bindgen",
···
3103
3104
[[package]]
3105
name = "rustls"
3106
+
version = "0.23.35"
3107
source = "registry+https://github.com/rust-lang/crates.io-index"
3108
+
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
3109
dependencies = [
3110
"once_cell",
3111
"ring",
···
3113
"rustls-webpki",
3114
"subtle",
3115
"zeroize",
3116
+
]
3117
+
3118
+
[[package]]
3119
+
name = "rustls-native-certs"
3120
+
version = "0.8.2"
3121
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3122
+
checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923"
3123
+
dependencies = [
3124
+
"openssl-probe",
3125
+
"rustls-pki-types",
3126
+
"schannel",
3127
+
"security-framework",
3128
]
3129
3130
[[package]]
···
3176
]
3177
3178
[[package]]
3179
+
name = "schannel"
3180
+
version = "0.1.28"
3181
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3182
+
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
3183
+
dependencies = [
3184
+
"windows-sys 0.61.2",
3185
+
]
3186
+
3187
+
[[package]]
3188
name = "schemars"
3189
version = "0.9.0"
3190
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3198
3199
[[package]]
3200
name = "schemars"
3201
+
version = "1.1.0"
3202
source = "registry+https://github.com/rust-lang/crates.io-index"
3203
+
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
3204
dependencies = [
3205
"dyn-clone",
3206
"ref-cast",
···
3209
]
3210
3211
[[package]]
3212
+
name = "scoped-tls"
3213
+
version = "1.0.1"
3214
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3215
+
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
3216
+
3217
+
[[package]]
3218
name = "scopeguard"
3219
version = "1.2.0"
3220
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3233
"subtle",
3234
"zeroize",
3235
]
3236
+
3237
+
[[package]]
3238
+
name = "security-framework"
3239
+
version = "3.5.1"
3240
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3241
+
checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
3242
+
dependencies = [
3243
+
"bitflags",
3244
+
"core-foundation 0.10.1",
3245
+
"core-foundation-sys",
3246
+
"libc",
3247
+
"security-framework-sys",
3248
+
]
3249
+
3250
+
[[package]]
3251
+
name = "security-framework-sys"
3252
+
version = "2.15.0"
3253
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3254
+
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
3255
+
dependencies = [
3256
+
"core-foundation-sys",
3257
+
"libc",
3258
+
]
3259
+
3260
+
[[package]]
3261
+
name = "send_wrapper"
3262
+
version = "0.6.0"
3263
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3264
+
checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
3265
3266
[[package]]
3267
name = "serde"
···
3300
dependencies = [
3301
"proc-macro2",
3302
"quote",
3303
+
"syn 2.0.110",
3304
]
3305
3306
[[package]]
···
3342
]
3343
3344
[[package]]
3345
+
name = "serde_path_to_error"
3346
+
version = "0.1.20"
3347
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3348
+
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
3349
+
dependencies = [
3350
+
"itoa",
3351
+
"serde",
3352
+
"serde_core",
3353
+
]
3354
+
3355
+
[[package]]
3356
name = "serde_repr"
3357
version = "0.1.20"
3358
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3360
dependencies = [
3361
"proc-macro2",
3362
"quote",
3363
+
"syn 2.0.110",
3364
]
3365
3366
[[package]]
···
3387
"indexmap 1.9.3",
3388
"indexmap 2.12.0",
3389
"schemars 0.9.0",
3390
+
"schemars 1.1.0",
3391
"serde_core",
3392
"serde_json",
3393
"serde_with_macros",
···
3403
"darling",
3404
"proc-macro2",
3405
"quote",
3406
+
"syn 2.0.110",
3407
+
]
3408
+
3409
+
[[package]]
3410
+
name = "sha1"
3411
+
version = "0.10.6"
3412
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3413
+
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
3414
+
dependencies = [
3415
+
"cfg-if",
3416
+
"cpufeatures",
3417
+
"digest",
3418
]
3419
3420
[[package]]
···
3432
"cfg-if",
3433
"cpufeatures",
3434
"digest",
3435
+
]
3436
+
3437
+
[[package]]
3438
+
name = "sharded-slab"
3439
+
version = "0.1.7"
3440
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3441
+
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
3442
+
dependencies = [
3443
+
"lazy_static",
3444
]
3445
3446
[[package]]
···
3538
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
3539
3540
[[package]]
3541
+
name = "spin"
3542
+
version = "0.10.0"
3543
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3544
+
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
3545
+
3546
+
[[package]]
3547
name = "spki"
3548
version = "0.7.3"
3549
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3575
"quote",
3576
"serde",
3577
"sha2",
3578
+
"syn 2.0.110",
3579
"thiserror 1.0.69",
3580
]
3581
···
3656
3657
[[package]]
3658
name = "syn"
3659
+
version = "2.0.110"
3660
source = "registry+https://github.com/rust-lang/crates.io-index"
3661
+
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
3662
dependencies = [
3663
"proc-macro2",
3664
"quote",
···
3682
dependencies = [
3683
"proc-macro2",
3684
"quote",
3685
+
"syn 2.0.110",
3686
]
3687
3688
[[package]]
···
3782
dependencies = [
3783
"proc-macro2",
3784
"quote",
3785
+
"syn 2.0.110",
3786
]
3787
3788
[[package]]
···
3793
dependencies = [
3794
"proc-macro2",
3795
"quote",
3796
+
"syn 2.0.110",
3797
+
]
3798
+
3799
+
[[package]]
3800
+
name = "thread_local"
3801
+
version = "1.1.9"
3802
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3803
+
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
3804
+
dependencies = [
3805
+
"cfg-if",
3806
]
3807
3808
[[package]]
···
3909
dependencies = [
3910
"proc-macro2",
3911
"quote",
3912
+
"syn 2.0.110",
3913
]
3914
3915
[[package]]
···
3923
]
3924
3925
[[package]]
3926
+
name = "tokio-tungstenite"
3927
+
version = "0.24.0"
3928
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3929
+
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
3930
+
dependencies = [
3931
+
"futures-util",
3932
+
"log",
3933
+
"rustls",
3934
+
"rustls-native-certs",
3935
+
"rustls-pki-types",
3936
+
"tokio",
3937
+
"tokio-rustls",
3938
+
"tungstenite",
3939
+
]
3940
+
3941
+
[[package]]
3942
+
name = "tokio-tungstenite-wasm"
3943
+
version = "0.4.0"
3944
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3945
+
checksum = "e21a5c399399c3db9f08d8297ac12b500e86bca82e930253fdc62eaf9c0de6ae"
3946
+
dependencies = [
3947
+
"futures-channel",
3948
+
"futures-util",
3949
+
"http",
3950
+
"httparse",
3951
+
"js-sys",
3952
+
"rustls",
3953
+
"thiserror 1.0.69",
3954
+
"tokio",
3955
+
"tokio-tungstenite",
3956
+
"wasm-bindgen",
3957
+
"web-sys",
3958
+
]
3959
+
3960
+
[[package]]
3961
name = "tokio-util"
3962
+
version = "0.7.17"
3963
source = "registry+https://github.com/rust-lang/crates.io-index"
3964
+
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
3965
dependencies = [
3966
"bytes",
3967
"futures-core",
3968
"futures-sink",
3969
+
"futures-util",
3970
"pin-project-lite",
3971
"tokio",
3972
]
3973
3974
[[package]]
3975
name = "tower"
3976
+
version = "0.4.13"
3977
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3978
+
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
3979
+
dependencies = [
3980
+
"tower-layer",
3981
+
"tower-service",
3982
+
"tracing",
3983
+
]
3984
+
3985
+
[[package]]
3986
+
name = "tower"
3987
version = "0.5.2"
3988
source = "registry+https://github.com/rust-lang/crates.io-index"
3989
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
···
3995
"tokio",
3996
"tower-layer",
3997
"tower-service",
3998
+
"tracing",
3999
+
]
4000
+
4001
+
[[package]]
4002
+
name = "tower-http"
4003
+
version = "0.5.2"
4004
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4005
+
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
4006
+
dependencies = [
4007
+
"async-compression",
4008
+
"bitflags",
4009
+
"bytes",
4010
+
"futures-core",
4011
+
"futures-util",
4012
+
"http",
4013
+
"http-body",
4014
+
"http-body-util",
4015
+
"http-range-header",
4016
+
"httpdate",
4017
+
"mime",
4018
+
"mime_guess",
4019
+
"percent-encoding",
4020
+
"pin-project-lite",
4021
+
"tokio",
4022
+
"tokio-util",
4023
+
"tower-layer",
4024
+
"tower-service",
4025
+
"tracing",
4026
]
4027
4028
[[package]]
···
4038
"http-body",
4039
"iri-string",
4040
"pin-project-lite",
4041
+
"tower 0.5.2",
4042
"tower-layer",
4043
"tower-service",
4044
]
···
4061
source = "registry+https://github.com/rust-lang/crates.io-index"
4062
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
4063
dependencies = [
4064
+
"log",
4065
"pin-project-lite",
4066
"tracing-attributes",
4067
"tracing-core",
···
4075
dependencies = [
4076
"proc-macro2",
4077
"quote",
4078
+
"syn 2.0.110",
4079
]
4080
4081
[[package]]
···
4085
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
4086
dependencies = [
4087
"once_cell",
4088
+
"valuable",
4089
+
]
4090
+
4091
+
[[package]]
4092
+
name = "tracing-log"
4093
+
version = "0.2.0"
4094
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4095
+
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
4096
+
dependencies = [
4097
+
"log",
4098
+
"once_cell",
4099
+
"tracing-core",
4100
+
]
4101
+
4102
+
[[package]]
4103
+
name = "tracing-subscriber"
4104
+
version = "0.3.20"
4105
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4106
+
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
4107
+
dependencies = [
4108
+
"matchers",
4109
+
"nu-ansi-term",
4110
+
"once_cell",
4111
+
"regex-automata",
4112
+
"sharded-slab",
4113
+
"smallvec",
4114
+
"thread_local",
4115
+
"tracing",
4116
+
"tracing-core",
4117
+
"tracing-log",
4118
]
4119
4120
[[package]]
···
4125
dependencies = [
4126
"proc-macro2",
4127
"quote",
4128
+
"syn 2.0.110",
4129
]
4130
4131
[[package]]
···
4141
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
4142
4143
[[package]]
4144
+
name = "tungstenite"
4145
+
version = "0.24.0"
4146
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4147
+
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
4148
+
dependencies = [
4149
+
"byteorder",
4150
+
"bytes",
4151
+
"data-encoding",
4152
+
"http",
4153
+
"httparse",
4154
+
"log",
4155
+
"rand 0.8.5",
4156
+
"rustls",
4157
+
"rustls-pki-types",
4158
+
"sha1",
4159
+
"thiserror 1.0.69",
4160
+
"utf-8",
4161
+
]
4162
+
4163
+
[[package]]
4164
name = "twoway"
4165
version = "0.1.8"
4166
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4212
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
4213
4214
[[package]]
4215
+
name = "unicode-xid"
4216
+
version = "0.2.6"
4217
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4218
+
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
4219
+
4220
+
[[package]]
4221
name = "unsigned-varint"
4222
version = "0.8.0"
4223
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4264
version = "0.2.2"
4265
source = "registry+https://github.com/rust-lang/crates.io-index"
4266
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
4267
+
4268
+
[[package]]
4269
+
name = "valuable"
4270
+
version = "0.1.1"
4271
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4272
+
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
4273
4274
[[package]]
4275
name = "version_check"
···
4356
"bumpalo",
4357
"proc-macro2",
4358
"quote",
4359
+
"syn 2.0.110",
4360
"wasm-bindgen-shared",
4361
]
4362
···
4455
]
4456
4457
[[package]]
4458
+
name = "windows"
4459
+
version = "0.61.3"
4460
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4461
+
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
4462
+
dependencies = [
4463
+
"windows-collections",
4464
+
"windows-core 0.61.2",
4465
+
"windows-future",
4466
+
"windows-link 0.1.3",
4467
+
"windows-numerics",
4468
+
]
4469
+
4470
+
[[package]]
4471
+
name = "windows-collections"
4472
+
version = "0.2.0"
4473
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4474
+
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
4475
+
dependencies = [
4476
+
"windows-core 0.61.2",
4477
+
]
4478
+
4479
+
[[package]]
4480
+
name = "windows-core"
4481
+
version = "0.61.2"
4482
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4483
+
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
4484
+
dependencies = [
4485
+
"windows-implement",
4486
+
"windows-interface",
4487
+
"windows-link 0.1.3",
4488
+
"windows-result 0.3.4",
4489
+
"windows-strings 0.4.2",
4490
+
]
4491
+
4492
+
[[package]]
4493
name = "windows-core"
4494
version = "0.62.2"
4495
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4503
]
4504
4505
[[package]]
4506
+
name = "windows-future"
4507
+
version = "0.2.1"
4508
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4509
+
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
4510
+
dependencies = [
4511
+
"windows-core 0.61.2",
4512
+
"windows-link 0.1.3",
4513
+
"windows-threading",
4514
+
]
4515
+
4516
+
[[package]]
4517
name = "windows-implement"
4518
version = "0.60.2"
4519
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4521
dependencies = [
4522
"proc-macro2",
4523
"quote",
4524
+
"syn 2.0.110",
4525
]
4526
4527
[[package]]
···
4532
dependencies = [
4533
"proc-macro2",
4534
"quote",
4535
+
"syn 2.0.110",
4536
]
4537
4538
[[package]]
···
4546
version = "0.2.1"
4547
source = "registry+https://github.com/rust-lang/crates.io-index"
4548
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
4549
+
4550
+
[[package]]
4551
+
name = "windows-numerics"
4552
+
version = "0.2.0"
4553
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4554
+
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
4555
+
dependencies = [
4556
+
"windows-core 0.61.2",
4557
+
"windows-link 0.1.3",
4558
+
]
4559
4560
[[package]]
4561
name = "windows-registry"
···
4713
]
4714
4715
[[package]]
4716
+
name = "windows-threading"
4717
+
version = "0.1.0"
4718
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4719
+
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
4720
+
dependencies = [
4721
+
"windows-link 0.1.3",
4722
+
]
4723
+
4724
+
[[package]]
4725
name = "windows_aarch64_gnullvm"
4726
version = "0.42.2"
4727
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4913
4914
[[package]]
4915
name = "wisp-cli"
4916
+
version = "0.2.0"
4917
dependencies = [
4918
+
"axum",
4919
"base64 0.22.1",
4920
"bytes",
4921
+
"chrono",
4922
"clap",
4923
"flate2",
4924
"futures",
···
4933
"mime_guess",
4934
"multibase",
4935
"multihash",
4936
+
"n0-future",
4937
"reqwest",
4938
"rustversion",
4939
"serde",
···
4941
"sha2",
4942
"shellexpand",
4943
"tokio",
4944
+
"tower 0.4.13",
4945
+
"tower-http 0.5.2",
4946
+
"url",
4947
"walkdir",
4948
]
4949
···
4995
dependencies = [
4996
"proc-macro2",
4997
"quote",
4998
+
"syn 2.0.110",
4999
"synstructure",
5000
]
5001
···
5016
dependencies = [
5017
"proc-macro2",
5018
"quote",
5019
+
"syn 2.0.110",
5020
]
5021
5022
[[package]]
···
5036
dependencies = [
5037
"proc-macro2",
5038
"quote",
5039
+
"syn 2.0.110",
5040
"synstructure",
5041
]
5042
···
5079
dependencies = [
5080
"proc-macro2",
5081
"quote",
5082
+
"syn 2.0.110",
5083
]
+8
-2
cli/Cargo.toml
+8
-2
cli/Cargo.toml
···
1
[package]
2
name = "wisp-cli"
3
-
version = "0.1.0"
4
edition = "2024"
5
6
[features]
···
11
jacquard = { git = "https://tangled.org/@nonbinary.computer/jacquard", features = ["loopback"] }
12
jacquard-oauth = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
13
jacquard-api = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
14
-
jacquard-common = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
15
jacquard-identity = { git = "https://tangled.org/@nonbinary.computer/jacquard", features = ["dns"] }
16
jacquard-derive = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
17
jacquard-lexicon = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
···
33
multihash = "0.19.3"
34
multibase = "0.9"
35
sha2 = "0.10"
···
1
[package]
2
name = "wisp-cli"
3
+
version = "0.2.0"
4
edition = "2024"
5
6
[features]
···
11
jacquard = { git = "https://tangled.org/@nonbinary.computer/jacquard", features = ["loopback"] }
12
jacquard-oauth = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
13
jacquard-api = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
14
+
jacquard-common = { git = "https://tangled.org/@nonbinary.computer/jacquard", features = ["websocket"] }
15
jacquard-identity = { git = "https://tangled.org/@nonbinary.computer/jacquard", features = ["dns"] }
16
jacquard-derive = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
17
jacquard-lexicon = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
···
33
multihash = "0.19.3"
34
multibase = "0.9"
35
sha2 = "0.10"
36
+
axum = "0.7"
37
+
tower-http = { version = "0.5", features = ["fs", "compression-gzip"] }
38
+
tower = "0.4"
39
+
n0-future = "0.1"
40
+
chrono = "0.4"
41
+
url = "2.5"
+71
cli/src/download.rs
+71
cli/src/download.rs
···
···
1
+
use base64::Engine;
2
+
use bytes::Bytes;
3
+
use flate2::read::GzDecoder;
4
+
use jacquard_common::types::blob::BlobRef;
5
+
use miette::IntoDiagnostic;
6
+
use std::io::Read;
7
+
use url::Url;
8
+
9
+
/// Download a blob from the PDS
10
+
pub async fn download_blob(pds_url: &Url, blob_ref: &BlobRef<'_>, did: &str) -> miette::Result<Bytes> {
11
+
// Extract CID from blob ref
12
+
let cid = blob_ref.blob().r#ref.to_string();
13
+
14
+
// Construct blob download URL
15
+
// The correct endpoint is: /xrpc/com.atproto.sync.getBlob?did={did}&cid={cid}
16
+
let blob_url = pds_url
17
+
.join(&format!("/xrpc/com.atproto.sync.getBlob?did={}&cid={}", did, cid))
18
+
.into_diagnostic()?;
19
+
20
+
let client = reqwest::Client::new();
21
+
let response = client
22
+
.get(blob_url)
23
+
.send()
24
+
.await
25
+
.into_diagnostic()?;
26
+
27
+
if !response.status().is_success() {
28
+
return Err(miette::miette!(
29
+
"Failed to download blob: {}",
30
+
response.status()
31
+
));
32
+
}
33
+
34
+
let bytes = response.bytes().await.into_diagnostic()?;
35
+
Ok(bytes)
36
+
}
37
+
38
+
/// Decompress and decode a blob (base64 + gzip)
39
+
pub fn decompress_blob(data: &[u8], is_base64: bool, is_gzipped: bool) -> miette::Result<Vec<u8>> {
40
+
let mut current_data = data.to_vec();
41
+
42
+
// First, decode base64 if needed
43
+
if is_base64 {
44
+
current_data = base64::prelude::BASE64_STANDARD
45
+
.decode(¤t_data)
46
+
.into_diagnostic()?;
47
+
}
48
+
49
+
// Then, decompress gzip if needed
50
+
if is_gzipped {
51
+
let mut decoder = GzDecoder::new(¤t_data[..]);
52
+
let mut decompressed = Vec::new();
53
+
decoder.read_to_end(&mut decompressed).into_diagnostic()?;
54
+
current_data = decompressed;
55
+
}
56
+
57
+
Ok(current_data)
58
+
}
59
+
60
+
/// Download and decompress a blob
61
+
pub async fn download_and_decompress_blob(
62
+
pds_url: &Url,
63
+
blob_ref: &BlobRef<'_>,
64
+
did: &str,
65
+
is_base64: bool,
66
+
is_gzipped: bool,
67
+
) -> miette::Result<Vec<u8>> {
68
+
let data = download_blob(pds_url, blob_ref, did).await?;
69
+
decompress_blob(&data, is_base64, is_gzipped)
70
+
}
71
+
+109
-16
cli/src/main.rs
+109
-16
cli/src/main.rs
···
2
mod place_wisp;
3
mod cid;
4
mod blob_map;
5
6
-
use clap::Parser;
7
use jacquard::CowStr;
8
use jacquard::client::{Agent, FileAuthStore, AgentSessionExt, MemoryCredentialSession, AgentSession};
9
use jacquard::oauth::client::OAuthClient;
···
23
use place_wisp::fs::*;
24
25
#[derive(Parser, Debug)]
26
-
#[command(author, version, about = "Deploy a static site to wisp.place")]
27
struct Args {
28
/// Handle (e.g., alice.bsky.social), DID, or PDS URL
29
-
input: CowStr<'static>,
30
31
/// Path to the directory containing your static site
32
-
#[arg(short, long, default_value = ".")]
33
-
path: PathBuf,
34
35
/// Site name (defaults to directory name)
36
-
#[arg(short, long)]
37
site: Option<String>,
38
39
-
/// Path to auth store file (will be created if missing, only used with OAuth)
40
-
#[arg(long, default_value = "/tmp/wisp-oauth-session.json")]
41
-
store: String,
42
43
-
/// App Password for authentication (alternative to OAuth)
44
-
#[arg(long)]
45
password: Option<CowStr<'static>>,
46
}
47
48
#[tokio::main]
49
async fn main() -> miette::Result<()> {
50
let args = Args::parse();
51
52
-
// Dispatch to appropriate authentication method
53
-
if let Some(password) = args.password {
54
-
run_with_app_password(args.input, password, args.path, args.site).await
55
-
} else {
56
-
run_with_oauth(args.input, args.store, args.path, args.site).await
57
}
58
}
59
···
2
mod place_wisp;
3
mod cid;
4
mod blob_map;
5
+
mod metadata;
6
+
mod download;
7
+
mod pull;
8
+
mod serve;
9
10
+
use clap::{Parser, Subcommand};
11
use jacquard::CowStr;
12
use jacquard::client::{Agent, FileAuthStore, AgentSessionExt, MemoryCredentialSession, AgentSession};
13
use jacquard::oauth::client::OAuthClient;
···
27
use place_wisp::fs::*;
28
29
#[derive(Parser, Debug)]
30
+
#[command(author, version, about = "wisp.place CLI tool")]
31
struct Args {
32
+
#[command(subcommand)]
33
+
command: Option<Commands>,
34
+
35
+
// Deploy arguments (when no subcommand is specified)
36
/// Handle (e.g., alice.bsky.social), DID, or PDS URL
37
+
#[arg(global = true, conflicts_with = "command")]
38
+
input: Option<CowStr<'static>>,
39
40
/// Path to the directory containing your static site
41
+
#[arg(short, long, global = true, conflicts_with = "command")]
42
+
path: Option<PathBuf>,
43
44
/// Site name (defaults to directory name)
45
+
#[arg(short, long, global = true, conflicts_with = "command")]
46
site: Option<String>,
47
48
+
/// Path to auth store file
49
+
#[arg(long, global = true, conflicts_with = "command")]
50
+
store: Option<String>,
51
52
+
/// App Password for authentication
53
+
#[arg(long, global = true, conflicts_with = "command")]
54
password: Option<CowStr<'static>>,
55
}
56
57
+
#[derive(Subcommand, Debug)]
58
+
enum Commands {
59
+
/// Deploy a static site to wisp.place (default command)
60
+
Deploy {
61
+
/// Handle (e.g., alice.bsky.social), DID, or PDS URL
62
+
input: CowStr<'static>,
63
+
64
+
/// Path to the directory containing your static site
65
+
#[arg(short, long, default_value = ".")]
66
+
path: PathBuf,
67
+
68
+
/// Site name (defaults to directory name)
69
+
#[arg(short, long)]
70
+
site: Option<String>,
71
+
72
+
/// Path to auth store file (will be created if missing, only used with OAuth)
73
+
#[arg(long, default_value = "/tmp/wisp-oauth-session.json")]
74
+
store: String,
75
+
76
+
/// App Password for authentication (alternative to OAuth)
77
+
#[arg(long)]
78
+
password: Option<CowStr<'static>>,
79
+
},
80
+
/// Pull a site from the PDS to a local directory
81
+
Pull {
82
+
/// Handle (e.g., alice.bsky.social) or DID
83
+
input: CowStr<'static>,
84
+
85
+
/// Site name (record key)
86
+
#[arg(short, long)]
87
+
site: String,
88
+
89
+
/// Output directory for the downloaded site
90
+
#[arg(short, long, default_value = ".")]
91
+
output: PathBuf,
92
+
},
93
+
/// Serve a site locally with real-time firehose updates
94
+
Serve {
95
+
/// Handle (e.g., alice.bsky.social) or DID
96
+
input: CowStr<'static>,
97
+
98
+
/// Site name (record key)
99
+
#[arg(short, long)]
100
+
site: String,
101
+
102
+
/// Output directory for the site files
103
+
#[arg(short, long, default_value = ".")]
104
+
output: PathBuf,
105
+
106
+
/// Port to serve on
107
+
#[arg(short, long, default_value = "8080")]
108
+
port: u16,
109
+
},
110
+
}
111
+
112
#[tokio::main]
113
async fn main() -> miette::Result<()> {
114
let args = Args::parse();
115
116
+
match args.command {
117
+
Some(Commands::Deploy { input, path, site, store, password }) => {
118
+
// Dispatch to appropriate authentication method
119
+
if let Some(password) = password {
120
+
run_with_app_password(input, password, path, site).await
121
+
} else {
122
+
run_with_oauth(input, store, path, site).await
123
+
}
124
+
}
125
+
Some(Commands::Pull { input, site, output }) => {
126
+
pull::pull_site(input, CowStr::from(site), output).await
127
+
}
128
+
Some(Commands::Serve { input, site, output, port }) => {
129
+
serve::serve_site(input, CowStr::from(site), output, port).await
130
+
}
131
+
None => {
132
+
// Legacy mode: if input is provided, assume deploy command
133
+
if let Some(input) = args.input {
134
+
let path = args.path.unwrap_or_else(|| PathBuf::from("."));
135
+
let store = args.store.unwrap_or_else(|| "/tmp/wisp-oauth-session.json".to_string());
136
+
137
+
// Dispatch to appropriate authentication method
138
+
if let Some(password) = args.password {
139
+
run_with_app_password(input, password, path, args.site).await
140
+
} else {
141
+
run_with_oauth(input, store, path, args.site).await
142
+
}
143
+
} else {
144
+
// No command and no input, show help
145
+
use clap::CommandFactory;
146
+
Args::command().print_help().into_diagnostic()?;
147
+
Ok(())
148
+
}
149
+
}
150
}
151
}
152
+46
cli/src/metadata.rs
+46
cli/src/metadata.rs
···
···
1
+
use serde::{Deserialize, Serialize};
2
+
use std::collections::HashMap;
3
+
use std::path::Path;
4
+
use miette::IntoDiagnostic;
5
+
6
+
/// Metadata tracking file CIDs for incremental updates
7
+
#[derive(Debug, Clone, Serialize, Deserialize)]
8
+
pub struct SiteMetadata {
9
+
/// Record CID from the PDS
10
+
pub record_cid: String,
11
+
/// Map of file paths to their blob CIDs
12
+
pub file_cids: HashMap<String, String>,
13
+
/// Timestamp when the site was last synced
14
+
pub last_sync: i64,
15
+
}
16
+
17
+
impl SiteMetadata {
18
+
pub fn new(record_cid: String, file_cids: HashMap<String, String>) -> Self {
19
+
Self {
20
+
record_cid,
21
+
file_cids,
22
+
last_sync: chrono::Utc::now().timestamp(),
23
+
}
24
+
}
25
+
26
+
/// Load metadata from a directory
27
+
pub fn load(dir: &Path) -> miette::Result<Option<Self>> {
28
+
let metadata_path = dir.join(".wisp-metadata.json");
29
+
if !metadata_path.exists() {
30
+
return Ok(None);
31
+
}
32
+
33
+
let contents = std::fs::read_to_string(&metadata_path).into_diagnostic()?;
34
+
let metadata: SiteMetadata = serde_json::from_str(&contents).into_diagnostic()?;
35
+
Ok(Some(metadata))
36
+
}
37
+
38
+
/// Save metadata to a directory
39
+
pub fn save(&self, dir: &Path) -> miette::Result<()> {
40
+
let metadata_path = dir.join(".wisp-metadata.json");
41
+
let contents = serde_json::to_string_pretty(self).into_diagnostic()?;
42
+
std::fs::write(&metadata_path, contents).into_diagnostic()?;
43
+
Ok(())
44
+
}
45
+
}
46
+
+305
cli/src/pull.rs
+305
cli/src/pull.rs
···
···
1
+
use crate::blob_map;
2
+
use crate::download;
3
+
use crate::metadata::SiteMetadata;
4
+
use crate::place_wisp::fs::*;
5
+
use jacquard::CowStr;
6
+
use jacquard::prelude::IdentityResolver;
7
+
use jacquard_common::types::string::Did;
8
+
use jacquard_common::xrpc::XrpcExt;
9
+
use jacquard_identity::PublicResolver;
10
+
use miette::IntoDiagnostic;
11
+
use std::collections::HashMap;
12
+
use std::path::{Path, PathBuf};
13
+
use url::Url;
14
+
15
+
/// Pull a site from the PDS to a local directory
16
+
pub async fn pull_site(
17
+
input: CowStr<'static>,
18
+
rkey: CowStr<'static>,
19
+
output_dir: PathBuf,
20
+
) -> miette::Result<()> {
21
+
println!("Pulling site {} from {}...", rkey, input);
22
+
23
+
// Resolve handle to DID if needed
24
+
let resolver = PublicResolver::default();
25
+
let did = if input.starts_with("did:") {
26
+
Did::new(&input).into_diagnostic()?
27
+
} else {
28
+
// It's a handle, resolve it
29
+
let handle = jacquard_common::types::string::Handle::new(&input).into_diagnostic()?;
30
+
resolver.resolve_handle(&handle).await.into_diagnostic()?
31
+
};
32
+
33
+
// Resolve PDS endpoint for the DID
34
+
let pds_url = resolver.pds_for_did(&did).await.into_diagnostic()?;
35
+
println!("Resolved PDS: {}", pds_url);
36
+
37
+
// Fetch the place.wisp.fs record
38
+
39
+
println!("Fetching record from PDS...");
40
+
let client = reqwest::Client::new();
41
+
42
+
// Use com.atproto.repo.getRecord
43
+
use jacquard::api::com_atproto::repo::get_record::GetRecord;
44
+
use jacquard_common::types::string::Rkey as RkeyType;
45
+
let rkey_parsed = RkeyType::new(&rkey).into_diagnostic()?;
46
+
47
+
use jacquard_common::types::ident::AtIdentifier;
48
+
use jacquard_common::types::string::RecordKey;
49
+
let request = GetRecord::new()
50
+
.repo(AtIdentifier::Did(did.clone()))
51
+
.collection(CowStr::from("place.wisp.fs"))
52
+
.rkey(RecordKey::from(rkey_parsed))
53
+
.build();
54
+
55
+
let response = client
56
+
.xrpc(pds_url.clone())
57
+
.send(&request)
58
+
.await
59
+
.into_diagnostic()?;
60
+
61
+
let record_output = response.into_output().into_diagnostic()?;
62
+
let record_cid = record_output.cid.as_ref().map(|c| c.to_string()).unwrap_or_default();
63
+
64
+
// Parse the record value as Fs
65
+
use jacquard_common::types::value::from_data;
66
+
let fs_record: Fs = from_data(&record_output.value).into_diagnostic()?;
67
+
68
+
let file_count = fs_record.file_count.map(|c| c.to_string()).unwrap_or_else(|| "?".to_string());
69
+
println!("Found site '{}' with {} files", fs_record.site, file_count);
70
+
71
+
// Load existing metadata for incremental updates
72
+
let existing_metadata = SiteMetadata::load(&output_dir)?;
73
+
let existing_file_cids = existing_metadata
74
+
.as_ref()
75
+
.map(|m| m.file_cids.clone())
76
+
.unwrap_or_default();
77
+
78
+
// Extract blob map from the new manifest
79
+
let new_blob_map = blob_map::extract_blob_map(&fs_record.root);
80
+
let new_file_cids: HashMap<String, String> = new_blob_map
81
+
.iter()
82
+
.map(|(path, (_blob_ref, cid))| (path.clone(), cid.clone()))
83
+
.collect();
84
+
85
+
// Clean up any leftover temp directories from previous failed attempts
86
+
let parent = output_dir.parent().unwrap_or_else(|| std::path::Path::new("."));
87
+
let output_name = output_dir.file_name().unwrap_or_else(|| std::ffi::OsStr::new("site")).to_string_lossy();
88
+
let temp_prefix = format!(".tmp-{}-", output_name);
89
+
90
+
if let Ok(entries) = parent.read_dir() {
91
+
for entry in entries.flatten() {
92
+
let name = entry.file_name();
93
+
if name.to_string_lossy().starts_with(&temp_prefix) {
94
+
let _ = std::fs::remove_dir_all(entry.path());
95
+
}
96
+
}
97
+
}
98
+
99
+
// Check if we need to update (but only if output directory actually exists with files)
100
+
if let Some(metadata) = &existing_metadata {
101
+
if metadata.record_cid == record_cid {
102
+
// Verify that the output directory actually exists and has content
103
+
let has_content = output_dir.exists() &&
104
+
output_dir.read_dir()
105
+
.map(|mut entries| entries.any(|e| {
106
+
if let Ok(entry) = e {
107
+
!entry.file_name().to_string_lossy().starts_with(".wisp-metadata")
108
+
} else {
109
+
false
110
+
}
111
+
}))
112
+
.unwrap_or(false);
113
+
114
+
if has_content {
115
+
println!("Site is already up to date!");
116
+
return Ok(());
117
+
}
118
+
}
119
+
}
120
+
121
+
// Create temporary directory for atomic update
122
+
// Place temp dir in parent directory to avoid issues with non-existent output_dir
123
+
let parent = output_dir.parent().unwrap_or_else(|| std::path::Path::new("."));
124
+
let temp_dir_name = format!(
125
+
".tmp-{}-{}",
126
+
output_dir.file_name().unwrap_or_else(|| std::ffi::OsStr::new("site")).to_string_lossy(),
127
+
chrono::Utc::now().timestamp()
128
+
);
129
+
let temp_dir = parent.join(temp_dir_name);
130
+
std::fs::create_dir_all(&temp_dir).into_diagnostic()?;
131
+
132
+
println!("Downloading files...");
133
+
let mut downloaded = 0;
134
+
let mut reused = 0;
135
+
136
+
// Download files recursively
137
+
let download_result = download_directory(
138
+
&fs_record.root,
139
+
&temp_dir,
140
+
&pds_url,
141
+
did.as_str(),
142
+
&new_blob_map,
143
+
&existing_file_cids,
144
+
&output_dir,
145
+
String::new(),
146
+
&mut downloaded,
147
+
&mut reused,
148
+
)
149
+
.await;
150
+
151
+
// If download failed, clean up temp directory
152
+
if let Err(e) = download_result {
153
+
let _ = std::fs::remove_dir_all(&temp_dir);
154
+
return Err(e);
155
+
}
156
+
157
+
println!(
158
+
"Downloaded {} files, reused {} files",
159
+
downloaded, reused
160
+
);
161
+
162
+
// Save metadata
163
+
let metadata = SiteMetadata::new(record_cid, new_file_cids);
164
+
metadata.save(&temp_dir)?;
165
+
166
+
// Move files from temp to output directory
167
+
let output_abs = std::fs::canonicalize(&output_dir).unwrap_or_else(|_| output_dir.clone());
168
+
let current_dir = std::env::current_dir().into_diagnostic()?;
169
+
170
+
// Special handling for pulling to current directory
171
+
if output_abs == current_dir {
172
+
// Move files from temp to current directory
173
+
for entry in std::fs::read_dir(&temp_dir).into_diagnostic()? {
174
+
let entry = entry.into_diagnostic()?;
175
+
let dest = current_dir.join(entry.file_name());
176
+
177
+
// Remove existing file/dir if it exists
178
+
if dest.exists() {
179
+
if dest.is_dir() {
180
+
std::fs::remove_dir_all(&dest).into_diagnostic()?;
181
+
} else {
182
+
std::fs::remove_file(&dest).into_diagnostic()?;
183
+
}
184
+
}
185
+
186
+
// Move from temp to current dir
187
+
std::fs::rename(entry.path(), dest).into_diagnostic()?;
188
+
}
189
+
190
+
// Clean up temp directory
191
+
std::fs::remove_dir_all(&temp_dir).into_diagnostic()?;
192
+
} else {
193
+
// If output directory exists and has content, remove it first
194
+
if output_dir.exists() {
195
+
std::fs::remove_dir_all(&output_dir).into_diagnostic()?;
196
+
}
197
+
198
+
// Ensure parent directory exists
199
+
if let Some(parent) = output_dir.parent() {
200
+
if !parent.as_os_str().is_empty() && !parent.exists() {
201
+
std::fs::create_dir_all(parent).into_diagnostic()?;
202
+
}
203
+
}
204
+
205
+
// Rename temp to final location
206
+
match std::fs::rename(&temp_dir, &output_dir) {
207
+
Ok(_) => {},
208
+
Err(e) => {
209
+
// Clean up temp directory on failure
210
+
let _ = std::fs::remove_dir_all(&temp_dir);
211
+
return Err(miette::miette!("Failed to move temp directory: {}", e));
212
+
}
213
+
}
214
+
}
215
+
216
+
println!("โ Site pulled successfully to {}", output_dir.display());
217
+
218
+
Ok(())
219
+
}
220
+
221
+
/// Recursively download a directory
222
+
fn download_directory<'a>(
223
+
dir: &'a Directory<'_>,
224
+
output_dir: &'a Path,
225
+
pds_url: &'a Url,
226
+
did: &'a str,
227
+
new_blob_map: &'a HashMap<String, (jacquard_common::types::blob::BlobRef<'static>, String)>,
228
+
existing_file_cids: &'a HashMap<String, String>,
229
+
existing_output_dir: &'a Path,
230
+
path_prefix: String,
231
+
downloaded: &'a mut usize,
232
+
reused: &'a mut usize,
233
+
) -> std::pin::Pin<Box<dyn std::future::Future<Output = miette::Result<()>> + Send + 'a>> {
234
+
Box::pin(async move {
235
+
for entry in &dir.entries {
236
+
let entry_name = entry.name.as_str();
237
+
let current_path = if path_prefix.is_empty() {
238
+
entry_name.to_string()
239
+
} else {
240
+
format!("{}/{}", path_prefix, entry_name)
241
+
};
242
+
243
+
match &entry.node {
244
+
EntryNode::File(file) => {
245
+
let output_path = output_dir.join(entry_name);
246
+
247
+
// Check if file CID matches existing
248
+
if let Some((_blob_ref, new_cid)) = new_blob_map.get(¤t_path) {
249
+
if let Some(existing_cid) = existing_file_cids.get(¤t_path) {
250
+
if existing_cid == new_cid {
251
+
// File unchanged, copy from existing directory
252
+
let existing_path = existing_output_dir.join(¤t_path);
253
+
if existing_path.exists() {
254
+
std::fs::copy(&existing_path, &output_path).into_diagnostic()?;
255
+
*reused += 1;
256
+
println!(" โ Reused {}", current_path);
257
+
continue;
258
+
}
259
+
}
260
+
}
261
+
}
262
+
263
+
// File is new or changed, download it
264
+
println!(" โ Downloading {}", current_path);
265
+
let data = download::download_and_decompress_blob(
266
+
pds_url,
267
+
&file.blob,
268
+
did,
269
+
file.base64.unwrap_or(false),
270
+
file.encoding.as_ref().map(|e| e.as_str() == "gzip").unwrap_or(false),
271
+
)
272
+
.await?;
273
+
274
+
std::fs::write(&output_path, data).into_diagnostic()?;
275
+
*downloaded += 1;
276
+
}
277
+
EntryNode::Directory(subdir) => {
278
+
let subdir_path = output_dir.join(entry_name);
279
+
std::fs::create_dir_all(&subdir_path).into_diagnostic()?;
280
+
281
+
download_directory(
282
+
subdir,
283
+
&subdir_path,
284
+
pds_url,
285
+
did,
286
+
new_blob_map,
287
+
existing_file_cids,
288
+
existing_output_dir,
289
+
current_path,
290
+
downloaded,
291
+
reused,
292
+
)
293
+
.await?;
294
+
}
295
+
EntryNode::Unknown(_) => {
296
+
// Skip unknown node types
297
+
println!(" โ Skipping unknown node type for {}", current_path);
298
+
}
299
+
}
300
+
}
301
+
302
+
Ok(())
303
+
})
304
+
}
305
+
+202
cli/src/serve.rs
+202
cli/src/serve.rs
···
···
1
+
use crate::pull::pull_site;
2
+
use axum::Router;
3
+
use jacquard::CowStr;
4
+
use jacquard_common::jetstream::{CommitOperation, JetstreamMessage, JetstreamParams};
5
+
use jacquard_common::types::string::Did;
6
+
use jacquard_common::xrpc::{SubscriptionClient, TungsteniteSubscriptionClient};
7
+
use miette::IntoDiagnostic;
8
+
use n0_future::StreamExt;
9
+
use std::path::PathBuf;
10
+
use std::sync::Arc;
11
+
use tokio::sync::RwLock;
12
+
use tower_http::compression::CompressionLayer;
13
+
use tower_http::services::ServeDir;
14
+
use url::Url;
15
+
16
+
/// Shared state for the server
17
+
#[derive(Clone)]
18
+
struct ServerState {
19
+
did: CowStr<'static>,
20
+
rkey: CowStr<'static>,
21
+
output_dir: PathBuf,
22
+
last_cid: Arc<RwLock<Option<String>>>,
23
+
}
24
+
25
+
/// Serve a site locally with real-time firehose updates
26
+
pub async fn serve_site(
27
+
input: CowStr<'static>,
28
+
rkey: CowStr<'static>,
29
+
output_dir: PathBuf,
30
+
port: u16,
31
+
) -> miette::Result<()> {
32
+
println!("Serving site {} from {} on port {}...", rkey, input, port);
33
+
34
+
// Resolve handle to DID if needed
35
+
use jacquard_identity::PublicResolver;
36
+
use jacquard::prelude::IdentityResolver;
37
+
38
+
let resolver = PublicResolver::default();
39
+
let did = if input.starts_with("did:") {
40
+
Did::new(&input).into_diagnostic()?
41
+
} else {
42
+
// It's a handle, resolve it
43
+
let handle = jacquard_common::types::string::Handle::new(&input).into_diagnostic()?;
44
+
resolver.resolve_handle(&handle).await.into_diagnostic()?
45
+
};
46
+
47
+
println!("Resolved to DID: {}", did.as_str());
48
+
49
+
// Create output directory if it doesn't exist
50
+
std::fs::create_dir_all(&output_dir).into_diagnostic()?;
51
+
52
+
// Initial pull of the site
53
+
println!("Performing initial pull...");
54
+
let did_str = CowStr::from(did.as_str().to_string());
55
+
pull_site(did_str.clone(), rkey.clone(), output_dir.clone()).await?;
56
+
57
+
// Create shared state
58
+
let state = ServerState {
59
+
did: did_str.clone(),
60
+
rkey: rkey.clone(),
61
+
output_dir: output_dir.clone(),
62
+
last_cid: Arc::new(RwLock::new(None)),
63
+
};
64
+
65
+
// Start firehose listener in background
66
+
let firehose_state = state.clone();
67
+
tokio::spawn(async move {
68
+
if let Err(e) = watch_firehose(firehose_state).await {
69
+
eprintln!("Firehose error: {}", e);
70
+
}
71
+
});
72
+
73
+
// Create HTTP server with gzip compression
74
+
let app = Router::new()
75
+
.fallback_service(
76
+
ServeDir::new(&output_dir)
77
+
.precompressed_gzip()
78
+
)
79
+
.layer(CompressionLayer::new())
80
+
.with_state(state);
81
+
82
+
let addr = format!("0.0.0.0:{}", port);
83
+
let listener = tokio::net::TcpListener::bind(&addr)
84
+
.await
85
+
.into_diagnostic()?;
86
+
87
+
println!("\nโ Server running at http://localhost:{}", port);
88
+
println!(" Watching for updates on the firehose...\n");
89
+
90
+
axum::serve(listener, app).await.into_diagnostic()?;
91
+
92
+
Ok(())
93
+
}
94
+
95
+
/// Watch the firehose for updates to the specific site
96
+
fn watch_firehose(state: ServerState) -> std::pin::Pin<Box<dyn std::future::Future<Output = miette::Result<()>> + Send>> {
97
+
Box::pin(async move {
98
+
let jetstream_url = Url::parse("wss://jetstream1.us-east.fire.hose.cam")
99
+
.into_diagnostic()?;
100
+
101
+
println!("[Firehose] Connecting to Jetstream...");
102
+
103
+
// Create subscription client
104
+
let client = TungsteniteSubscriptionClient::from_base_uri(jetstream_url);
105
+
106
+
// Subscribe with no filters (we'll filter manually)
107
+
// Jetstream doesn't support filtering by collection in the params builder
108
+
let params = JetstreamParams::new().build();
109
+
110
+
let stream = client.subscribe(¶ms).await.into_diagnostic()?;
111
+
println!("[Firehose] Connected! Watching for updates...");
112
+
113
+
// Convert to typed message stream
114
+
let (_sink, mut messages) = stream.into_stream();
115
+
116
+
loop {
117
+
match messages.next().await {
118
+
Some(Ok(msg)) => {
119
+
if let Err(e) = handle_firehose_message(&state, msg).await {
120
+
eprintln!("[Firehose] Error handling message: {}", e);
121
+
}
122
+
}
123
+
Some(Err(e)) => {
124
+
eprintln!("[Firehose] Stream error: {}", e);
125
+
// Try to reconnect after a delay
126
+
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
127
+
return Box::pin(watch_firehose(state)).await;
128
+
}
129
+
None => {
130
+
println!("[Firehose] Stream ended, reconnecting...");
131
+
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
132
+
return Box::pin(watch_firehose(state)).await;
133
+
}
134
+
}
135
+
}
136
+
})
137
+
}
138
+
139
+
/// Handle a firehose message
140
+
async fn handle_firehose_message(
141
+
state: &ServerState,
142
+
msg: JetstreamMessage<'_>,
143
+
) -> miette::Result<()> {
144
+
match msg {
145
+
JetstreamMessage::Commit {
146
+
did,
147
+
commit,
148
+
..
149
+
} => {
150
+
// Check if this is our site
151
+
if did.as_str() == state.did.as_str()
152
+
&& commit.collection.as_str() == "place.wisp.fs"
153
+
&& commit.rkey.as_str() == state.rkey.as_str()
154
+
{
155
+
match commit.operation {
156
+
CommitOperation::Create | CommitOperation::Update => {
157
+
let new_cid = commit.cid.as_ref().map(|c| c.to_string());
158
+
159
+
// Check if CID changed
160
+
let should_update = {
161
+
let last_cid = state.last_cid.read().await;
162
+
new_cid != *last_cid
163
+
};
164
+
165
+
if should_update {
166
+
println!("\n[Update] Detected change to site {} (CID: {:?})", state.rkey, new_cid);
167
+
println!("[Update] Pulling latest version...");
168
+
169
+
// Pull the updated site
170
+
match pull_site(
171
+
state.did.clone(),
172
+
state.rkey.clone(),
173
+
state.output_dir.clone(),
174
+
)
175
+
.await
176
+
{
177
+
Ok(_) => {
178
+
// Update last CID
179
+
let mut last_cid = state.last_cid.write().await;
180
+
*last_cid = new_cid;
181
+
println!("[Update] โ Site updated successfully!\n");
182
+
}
183
+
Err(e) => {
184
+
eprintln!("[Update] Failed to pull site: {}", e);
185
+
}
186
+
}
187
+
}
188
+
}
189
+
CommitOperation::Delete => {
190
+
println!("\n[Update] Site {} was deleted", state.rkey);
191
+
}
192
+
}
193
+
}
194
+
}
195
+
_ => {
196
+
// Ignore identity and account messages
197
+
}
198
+
}
199
+
200
+
Ok(())
201
+
}
202
+
+28
-1
crates.nix
+28
-1
crates.nix
···
19
targets.x86_64-pc-windows-gnu.latest.rust-std
20
targets.x86_64-unknown-linux-gnu.latest.rust-std
21
targets.aarch64-apple-darwin.latest.rust-std
22
];
23
# configure crates
24
nci.crates."wisp-cli" = {
···
26
dev.runTests = false;
27
release.runTests = false;
28
};
29
-
targets."x86_64-unknown-linux-gnu" = {
30
default = true;
31
};
32
targets."x86_64-pc-windows-gnu" = let
33
targetPkgs = pkgs.pkgsCross.mingwW64;
···
46
};
47
targets."aarch64-apple-darwin" = let
48
targetPkgs = pkgs.pkgsCross.aarch64-darwin;
49
targetCC = targetPkgs.stdenv.cc;
50
targetCargoEnvVarTarget = targetPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget;
51
in rec {
···
19
targets.x86_64-pc-windows-gnu.latest.rust-std
20
targets.x86_64-unknown-linux-gnu.latest.rust-std
21
targets.aarch64-apple-darwin.latest.rust-std
22
+
targets.aarch64-unknown-linux-gnu.latest.rust-std
23
];
24
# configure crates
25
nci.crates."wisp-cli" = {
···
27
dev.runTests = false;
28
release.runTests = false;
29
};
30
+
targets."x86_64-unknown-linux-gnu" = let
31
+
targetPkgs = pkgs.pkgsCross.gnu64;
32
+
targetCC = targetPkgs.stdenv.cc;
33
+
targetCargoEnvVarTarget = targetPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget;
34
+
in rec {
35
default = true;
36
+
depsDrvConfig.mkDerivation = {
37
+
nativeBuildInputs = [targetCC];
38
+
};
39
+
depsDrvConfig.env = rec {
40
+
TARGET_CC = "${targetCC.targetPrefix}cc";
41
+
"CARGO_TARGET_${targetCargoEnvVarTarget}_LINKER" = TARGET_CC;
42
+
};
43
+
drvConfig = depsDrvConfig;
44
};
45
targets."x86_64-pc-windows-gnu" = let
46
targetPkgs = pkgs.pkgsCross.mingwW64;
···
59
};
60
targets."aarch64-apple-darwin" = let
61
targetPkgs = pkgs.pkgsCross.aarch64-darwin;
62
+
targetCC = targetPkgs.stdenv.cc;
63
+
targetCargoEnvVarTarget = targetPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget;
64
+
in rec {
65
+
depsDrvConfig.mkDerivation = {
66
+
nativeBuildInputs = [targetCC];
67
+
};
68
+
depsDrvConfig.env = rec {
69
+
TARGET_CC = "${targetCC.targetPrefix}cc";
70
+
"CARGO_TARGET_${targetCargoEnvVarTarget}_LINKER" = TARGET_CC;
71
+
};
72
+
drvConfig = depsDrvConfig;
73
+
};
74
+
targets."aarch64-unknown-linux-gnu" = let
75
+
targetPkgs = pkgs.pkgsCross.aarch64-multiplatform;
76
targetCC = targetPkgs.stdenv.cc;
77
targetCargoEnvVarTarget = targetPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget;
78
in rec {
+25
-2
flake.nix
+25
-2
flake.nix
···
26
...
27
}: let
28
crateOutputs = config.nci.outputs."wisp-cli";
29
in {
30
devShells.default = crateOutputs.devShell;
31
packages.default = crateOutputs.packages.release;
32
-
packages.wisp-cli-windows = crateOutputs.allTargets."x86_64-pc-windows-gnu".packages.release;
33
-
packages.wisp-cli-darwin = crateOutputs.allTargets."aarch64-apple-darwin".packages.release;
34
};
35
};
36
}
···
26
...
27
}: let
28
crateOutputs = config.nci.outputs."wisp-cli";
29
+
mkRenamedPackage = name: pkg: isWindows: pkgs.runCommand name {} ''
30
+
mkdir -p $out/bin
31
+
if [ -f ${pkg}/bin/wisp-cli.exe ]; then
32
+
cp ${pkg}/bin/wisp-cli.exe $out/bin/${name}
33
+
elif [ -f ${pkg}/bin/wisp-cli ]; then
34
+
cp ${pkg}/bin/wisp-cli $out/bin/${name}
35
+
else
36
+
echo "Error: Could not find wisp-cli binary in ${pkg}/bin/"
37
+
ls -la ${pkg}/bin/ || true
38
+
exit 1
39
+
fi
40
+
'';
41
in {
42
devShells.default = crateOutputs.devShell;
43
packages.default = crateOutputs.packages.release;
44
+
packages.wisp-cli-x86_64-linux = mkRenamedPackage "wisp-cli-x86_64-linux" crateOutputs.packages.release false;
45
+
packages.wisp-cli-aarch64-linux = mkRenamedPackage "wisp-cli-aarch64-linux" crateOutputs.allTargets."aarch64-unknown-linux-gnu".packages.release false;
46
+
packages.wisp-cli-x86_64-windows = mkRenamedPackage "wisp-cli-x86_64-windows.exe" crateOutputs.allTargets."x86_64-pc-windows-gnu".packages.release true;
47
+
packages.wisp-cli-aarch64-darwin = mkRenamedPackage "wisp-cli-aarch64-darwin" crateOutputs.allTargets."aarch64-apple-darwin".packages.release false;
48
+
packages.all = pkgs.symlinkJoin {
49
+
name = "wisp-cli-all";
50
+
paths = [
51
+
config.packages.wisp-cli-x86_64-linux
52
+
config.packages.wisp-cli-aarch64-linux
53
+
config.packages.wisp-cli-x86_64-windows
54
+
config.packages.wisp-cli-aarch64-darwin
55
+
];
56
+
};
57
};
58
};
59
}
+78
-14
public/editor/tabs/CLITab.tsx
+78
-14
public/editor/tabs/CLITab.tsx
···
16
<CardHeader>
17
<div className="flex items-center gap-2 mb-2">
18
<CardTitle>Wisp CLI Tool</CardTitle>
19
-
<Badge variant="secondary" className="text-xs">v0.1.0</Badge>
20
<Badge variant="outline" className="text-xs">Alpha</Badge>
21
</div>
22
<CardDescription>
···
32
</div>
33
34
<div className="space-y-3">
35
-
<h3 className="text-sm font-semibold">Download CLI</h3>
36
<div className="grid gap-2">
37
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
38
<a
39
-
href="https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-macos-arm64"
40
target="_blank"
41
rel="noopener noreferrer"
42
className="flex items-center justify-between mb-2"
···
45
<ExternalLink className="w-4 h-4 text-muted-foreground" />
46
</a>
47
<div className="text-xs text-muted-foreground">
48
-
<span className="font-mono">SHA256: 637e325d9668ca745e01493d80dfc72447ef0a889b313e28913ca65c94c7aaae</span>
49
</div>
50
</div>
51
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
···
59
<ExternalLink className="w-4 h-4 text-muted-foreground" />
60
</a>
61
<div className="text-xs text-muted-foreground">
62
-
<span className="font-mono">SHA256: 01561656b64826f95b39f13c65c97da8bcc63ecd9f4d7e4e369c8ba8c903c22a</span>
63
</div>
64
</div>
65
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
···
73
<ExternalLink className="w-4 h-4 text-muted-foreground" />
74
</a>
75
<div className="text-xs text-muted-foreground">
76
-
<span className="font-mono">SHA256: 1ff485b9bcf89bc5721a862863c4843cf4530cbcd2489cf200cb24a44f7865a2</span>
77
</div>
78
</div>
79
</div>
80
</div>
81
82
<div className="space-y-3">
83
-
<h3 className="text-sm font-semibold">Basic Usage</h3>
84
<CodeBlock
85
code={`# Download and make executable
86
-
curl -O https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-macos-arm64
87
-
chmod +x wisp-cli-macos-arm64
88
89
-
# Deploy your site (will use OAuth)
90
-
./wisp-cli-macos-arm64 your-handle.bsky.social \\
91
--path ./dist \\
92
-
--site my-site
93
94
# Your site will be available at:
95
# https://sites.wisp.place/your-handle/my-site`}
···
98
</div>
99
100
<div className="space-y-3">
101
<h3 className="text-sm font-semibold">CI/CD with Tangled Spindle</h3>
102
<p className="text-xs text-muted-foreground">
103
Deploy automatically on every push using{' '}
···
147
chmod +x wisp-cli
148
149
# Deploy to Wisp
150
-
./wisp-cli \\
151
"$WISP_HANDLE" \\
152
--path "$SITE_PATH" \\
153
--site "$SITE_NAME" \\
···
210
chmod +x wisp-cli
211
212
# Deploy to Wisp
213
-
./wisp-cli \\
214
"$WISP_HANDLE" \\
215
--path "$SITE_PATH" \\
216
--site "$SITE_NAME" \\
···
16
<CardHeader>
17
<div className="flex items-center gap-2 mb-2">
18
<CardTitle>Wisp CLI Tool</CardTitle>
19
+
<Badge variant="secondary" className="text-xs">v0.2.0</Badge>
20
<Badge variant="outline" className="text-xs">Alpha</Badge>
21
</div>
22
<CardDescription>
···
32
</div>
33
34
<div className="space-y-3">
35
+
<h3 className="text-sm font-semibold">Features</h3>
36
+
<ul className="text-sm text-muted-foreground space-y-2 list-disc list-inside">
37
+
<li><strong>Deploy:</strong> Push static sites directly from your terminal</li>
38
+
<li><strong>Pull:</strong> Download sites from the PDS for development or backup</li>
39
+
<li><strong>Serve:</strong> Run a local server with real-time firehose updates</li>
40
+
</ul>
41
+
</div>
42
+
43
+
<div className="space-y-3">
44
+
<h3 className="text-sm font-semibold">Download v0.2.0</h3>
45
<div className="grid gap-2">
46
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
47
<a
48
+
href="https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-aarch64-darwin"
49
target="_blank"
50
rel="noopener noreferrer"
51
className="flex items-center justify-between mb-2"
···
54
<ExternalLink className="w-4 h-4 text-muted-foreground" />
55
</a>
56
<div className="text-xs text-muted-foreground">
57
+
<span className="font-mono">SHA-1: a8c27ea41c5e2672bfecb3476ece1c801741d759</span>
58
</div>
59
</div>
60
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
···
68
<ExternalLink className="w-4 h-4 text-muted-foreground" />
69
</a>
70
<div className="text-xs text-muted-foreground">
71
+
<span className="font-mono">SHA-1: fd7ee689c7600fc953179ea755b0357c8481a622</span>
72
</div>
73
</div>
74
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
···
82
<ExternalLink className="w-4 h-4 text-muted-foreground" />
83
</a>
84
<div className="text-xs text-muted-foreground">
85
+
<span className="font-mono">SHA-1: 8bca6992559e19e1d29ab3d2fcc6d09b28e5a485</span>
86
+
</div>
87
+
</div>
88
+
<div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border">
89
+
<a
90
+
href="https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-x86_64-windows.exe"
91
+
target="_blank"
92
+
rel="noopener noreferrer"
93
+
className="flex items-center justify-between mb-2"
94
+
>
95
+
<span className="font-mono text-sm">Windows (x86_64)</span>
96
+
<ExternalLink className="w-4 h-4 text-muted-foreground" />
97
+
</a>
98
+
<div className="text-xs text-muted-foreground">
99
+
<span className="font-mono">SHA-1: 90ea3987a06597fa6c42e1df9009e9758e92dd54</span>
100
</div>
101
</div>
102
</div>
103
</div>
104
105
<div className="space-y-3">
106
+
<h3 className="text-sm font-semibold">Deploy a Site</h3>
107
<CodeBlock
108
code={`# Download and make executable
109
+
curl -O https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-aarch64-darwin
110
+
chmod +x wisp-cli-aarch64-darwin
111
112
+
# Deploy your site
113
+
./wisp-cli-aarch64-darwin deploy your-handle.bsky.social \\
114
--path ./dist \\
115
+
--site my-site \\
116
+
--password your-app-password
117
118
# Your site will be available at:
119
# https://sites.wisp.place/your-handle/my-site`}
···
122
</div>
123
124
<div className="space-y-3">
125
+
<h3 className="text-sm font-semibold">Pull a Site from PDS</h3>
126
+
<p className="text-xs text-muted-foreground">
127
+
Download a site from the PDS to your local machine (uses OAuth authentication):
128
+
</p>
129
+
<CodeBlock
130
+
code={`# Pull a site to a specific directory
131
+
wisp-cli pull your-handle.bsky.social \\
132
+
--site my-site \\
133
+
--output ./my-site
134
+
135
+
# Pull to current directory
136
+
wisp-cli pull your-handle.bsky.social \\
137
+
--site my-site
138
+
139
+
# Opens browser for OAuth authentication on first run`}
140
+
language="bash"
141
+
/>
142
+
</div>
143
+
144
+
<div className="space-y-3">
145
+
<h3 className="text-sm font-semibold">Serve a Site Locally with Real-Time Updates</h3>
146
+
<p className="text-xs text-muted-foreground">
147
+
Run a local server that monitors the firehose for real-time updates (uses OAuth authentication):
148
+
</p>
149
+
<CodeBlock
150
+
code={`# Serve on http://localhost:8080 (default)
151
+
wisp-cli serve your-handle.bsky.social \\
152
+
--site my-site
153
+
154
+
# Serve on a custom port
155
+
wisp-cli serve your-handle.bsky.social \\
156
+
--site my-site \\
157
+
--port 3000
158
+
159
+
# Downloads site, serves it, and watches firehose for live updates!`}
160
+
language="bash"
161
+
/>
162
+
</div>
163
+
164
+
<div className="space-y-3">
165
<h3 className="text-sm font-semibold">CI/CD with Tangled Spindle</h3>
166
<p className="text-xs text-muted-foreground">
167
Deploy automatically on every push using{' '}
···
211
chmod +x wisp-cli
212
213
# Deploy to Wisp
214
+
./wisp-cli deploy \\
215
"$WISP_HANDLE" \\
216
--path "$SITE_PATH" \\
217
--site "$SITE_NAME" \\
···
274
chmod +x wisp-cli
275
276
# Deploy to Wisp
277
+
./wisp-cli deploy \\
278
"$WISP_HANDLE" \\
279
--path "$SITE_PATH" \\
280
--site "$SITE_NAME" \\
+5
-5
src/index.ts
+5
-5
src/index.ts
···
70
},
71
cookie: {
72
secrets: cookieSecret,
73
-
sign: true
74
}
75
})
76
// Observability middleware
···
105
.onError(observabilityMiddleware('main-app').onError)
106
.use(csrfProtection())
107
.use(authRoutes(client, cookieSecret))
108
-
.use(wispRoutes(client))
109
-
.use(domainRoutes(client))
110
-
.use(userRoutes(client))
111
-
.use(siteRoutes(client))
112
.use(adminRoutes(cookieSecret))
113
.use(
114
await staticPlugin({
···
70
},
71
cookie: {
72
secrets: cookieSecret,
73
+
sign: ['did']
74
}
75
})
76
// Observability middleware
···
105
.onError(observabilityMiddleware('main-app').onError)
106
.use(csrfProtection())
107
.use(authRoutes(client, cookieSecret))
108
+
.use(wispRoutes(client, cookieSecret))
109
+
.use(domainRoutes(client, cookieSecret))
110
+
.use(userRoutes(client, cookieSecret))
111
+
.use(siteRoutes(client, cookieSecret))
112
.use(adminRoutes(cookieSecret))
113
.use(
114
await staticPlugin({
+6
-22
src/routes/auth.ts
+6
-22
src/routes/auth.ts
···
5
import { authenticateRequest } from '../lib/wisp-auth'
6
import { logger } from '../lib/observability'
7
8
-
export const authRoutes = (client: NodeOAuthClient, cookieSecret: string) => new Elysia()
9
.post('/api/auth/signin', async (c) => {
10
let handle = 'unknown'
11
try {
···
74
c.cookie.did.remove()
75
return c.redirect('/?error=auth_failed')
76
}
77
-
}, {
78
-
cookie: t.Cookie({
79
-
did: t.Optional(t.String())
80
-
}, {
81
-
secrets: cookieSecret,
82
-
sign: ['did']
83
-
})
84
})
85
.post('/api/auth/logout', async (c) => {
86
try {
···
106
logger.error('[Auth] Logout error', err)
107
return { error: 'Logout failed' }
108
}
109
-
}, {
110
-
cookie: t.Cookie({
111
-
did: t.Optional(t.String())
112
-
}, {
113
-
secrets: cookieSecret,
114
-
sign: ['did']
115
-
})
116
})
117
.get('/api/auth/status', async (c) => {
118
try {
···
132
c.cookie.did.remove()
133
return { authenticated: false }
134
}
135
-
}, {
136
-
cookie: t.Cookie({
137
-
did: t.Optional(t.String())
138
-
}, {
139
-
secrets: cookieSecret,
140
-
sign: ['did']
141
-
})
142
})
···
5
import { authenticateRequest } from '../lib/wisp-auth'
6
import { logger } from '../lib/observability'
7
8
+
export const authRoutes = (client: NodeOAuthClient, cookieSecret: string) => new Elysia({
9
+
cookie: {
10
+
secrets: cookieSecret,
11
+
sign: ['did']
12
+
}
13
+
})
14
.post('/api/auth/signin', async (c) => {
15
let handle = 'unknown'
16
try {
···
79
c.cookie.did.remove()
80
return c.redirect('/?error=auth_failed')
81
}
82
})
83
.post('/api/auth/logout', async (c) => {
84
try {
···
104
logger.error('[Auth] Logout error', err)
105
return { error: 'Logout failed' }
106
}
107
})
108
.get('/api/auth/status', async (c) => {
109
try {
···
123
c.cookie.did.remove()
124
return { authenticated: false }
125
}
126
})
+8
-2
src/routes/domain.ts
+8
-2
src/routes/domain.ts
···
24
import { verifyCustomDomain } from '../lib/dns-verify'
25
import { logger } from '../lib/logger'
26
27
-
export const domainRoutes = (client: NodeOAuthClient) =>
28
-
new Elysia({ prefix: '/api/domain' })
29
// Public endpoints (no auth required)
30
.get('/check', async ({ query }) => {
31
try {
···
24
import { verifyCustomDomain } from '../lib/dns-verify'
25
import { logger } from '../lib/logger'
26
27
+
export const domainRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
28
+
new Elysia({
29
+
prefix: '/api/domain',
30
+
cookie: {
31
+
secrets: cookieSecret,
32
+
sign: ['did']
33
+
}
34
+
})
35
// Public endpoints (no auth required)
36
.get('/check', async ({ query }) => {
37
try {
+8
-2
src/routes/site.ts
+8
-2
src/routes/site.ts
···
5
import { deleteSite } from '../lib/db'
6
import { logger } from '../lib/logger'
7
8
+
export const siteRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
9
+
new Elysia({
10
+
prefix: '/api/site',
11
+
cookie: {
12
+
secrets: cookieSecret,
13
+
sign: ['did']
14
+
}
15
+
})
16
.derive(async ({ cookie }) => {
17
const auth = await requireAuth(client, cookie)
18
return { auth }
+9
-3
src/routes/user.ts
+9
-3
src/routes/user.ts
···
1
-
import { Elysia } from 'elysia'
2
import { requireAuth } from '../lib/wisp-auth'
3
import { NodeOAuthClient } from '@atproto/oauth-client-node'
4
import { Agent } from '@atproto/api'
···
6
import { syncSitesFromPDS } from '../lib/sync-sites'
7
import { logger } from '../lib/logger'
8
9
-
export const userRoutes = (client: NodeOAuthClient) =>
10
-
new Elysia({ prefix: '/api/user' })
11
.derive(async ({ cookie }) => {
12
const auth = await requireAuth(client, cookie)
13
return { auth }
···
1
+
import { Elysia, t } from 'elysia'
2
import { requireAuth } from '../lib/wisp-auth'
3
import { NodeOAuthClient } from '@atproto/oauth-client-node'
4
import { Agent } from '@atproto/api'
···
6
import { syncSitesFromPDS } from '../lib/sync-sites'
7
import { logger } from '../lib/logger'
8
9
+
export const userRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
10
+
new Elysia({
11
+
prefix: '/api/user',
12
+
cookie: {
13
+
secrets: cookieSecret,
14
+
sign: ['did']
15
+
}
16
+
})
17
.derive(async ({ cookie }) => {
18
const auth = await requireAuth(client, cookie)
19
return { auth }
+8
-2
src/routes/wisp.ts
+8
-2
src/routes/wisp.ts
···
37
return true;
38
}
39
40
+
export const wispRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
41
+
new Elysia({
42
+
prefix: '/wisp',
43
+
cookie: {
44
+
secrets: cookieSecret,
45
+
sign: ['did']
46
+
}
47
+
})
48
.derive(async ({ cookie }) => {
49
const auth = await requireAuth(client, cookie)
50
return { auth }