fedipod-server 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/dist/door.d.ts +4 -0
- package/dist/door.js +31 -0
- package/dist/fedipod.d.ts +16 -0
- package/dist/fedipod.js +13 -0
- package/dist/front.d.ts +3 -0
- package/dist/front.js +94 -0
- package/dist/handler.d.ts +36 -101
- package/dist/handler.js +85 -645
- package/dist/handler.jsonld +2 -30
- package/dist/identities.d.ts +62 -0
- package/dist/identities.js +236 -0
- package/dist/mounts.d.ts +43 -0
- package/dist/mounts.js +123 -0
- package/dist/opt-in.d.ts +46 -0
- package/dist/opt-in.js +176 -0
- package/package.json +10 -18
- package/lib/client/c2s.mjs +0 -636
- package/lib/client/localapi.mjs +0 -87
- package/lib/client/masto/accounts.mjs +0 -310
- package/lib/client/masto/body.mjs +0 -156
- package/lib/client/masto/index.mjs +0 -206
- package/lib/client/masto/instance.mjs +0 -112
- package/lib/client/masto/media.mjs +0 -79
- package/lib/client/masto/oauth.mjs +0 -656
- package/lib/client/masto/render.mjs +0 -518
- package/lib/client/masto/statuses.mjs +0 -372
- package/lib/client/masto/timelines.mjs +0 -438
- package/lib/client/oidc-auth.mjs +0 -62
- package/lib/client/streaming.mjs +0 -154
- package/lib/client/webpush.mjs +0 -98
- package/lib/connections/acctfeed.mjs +0 -257
- package/lib/connections/atproto.mjs +0 -309
- package/lib/connections/bskyfeed.mjs +0 -243
- package/lib/connections/bskygroup.mjs +0 -162
- package/lib/connections/fediacct.mjs +0 -253
- package/lib/connections/import.mjs +0 -357
- package/lib/connections/tagfeed.mjs +0 -212
- package/lib/connections/vault.mjs +0 -114
- package/lib/core/as2.mjs +0 -206
- package/lib/core/contexts/activitystreams.json +0 -379
- package/lib/core/contexts/anno.json +0 -126
- package/lib/core/contexts/did-v1.json +0 -57
- package/lib/core/contexts/fep-5711.json +0 -36
- package/lib/core/contexts/gotosocial.json +0 -86
- package/lib/core/contexts/identity-v1.json +0 -152
- package/lib/core/contexts/index.mjs +0 -63
- package/lib/core/contexts/join-lemmy.json +0 -33
- package/lib/core/contexts/joinmastodon.json +0 -28
- package/lib/core/contexts/litepub-0.1.json +0 -60
- package/lib/core/contexts/map.json +0 -19
- package/lib/core/contexts/miscellany.json +0 -19
- package/lib/core/contexts/quotes.json +0 -18
- package/lib/core/contexts/schemaorg.json +0 -8845
- package/lib/core/contexts/security-data-integrity-v1.json +0 -78
- package/lib/core/contexts/security-data-integrity-v2.json +0 -81
- package/lib/core/contexts/security-multikey-v1.json +0 -35
- package/lib/core/contexts/security-v1.json +0 -74
- package/lib/core/contexts/webfinger.json +0 -10
- package/lib/core/deliver.mjs +0 -387
- package/lib/core/graphview.mjs +0 -327
- package/lib/core/intake/activities.mjs +0 -649
- package/lib/core/intake/activity.mjs +0 -318
- package/lib/core/intake/channel.mjs +0 -144
- package/lib/core/intake/group.mjs +0 -258
- package/lib/core/intake/index.mjs +0 -732
- package/lib/core/intake/notes.mjs +0 -367
- package/lib/core/intake/verify.mjs +0 -142
- package/lib/core/keys.mjs +0 -153
- package/lib/core/lease.mjs +0 -206
- package/lib/core/place.mjs +0 -82
- package/lib/core/polls.mjs +0 -105
- package/lib/core/profile-page.mjs +0 -104
- package/lib/core/proof.mjs +0 -79
- package/lib/core/publisher/collections.mjs +0 -272
- package/lib/core/publisher/index.mjs +0 -522
- package/lib/core/publisher/notes.mjs +0 -519
- package/lib/core/publisher/own.mjs +0 -143
- package/lib/core/publisher/questions.mjs +0 -241
- package/lib/core/publisher/restore.mjs +0 -224
- package/lib/core/shapes/activitystreams.ttl +0 -157
- package/lib/core/shapes/index.mjs +0 -132
- package/lib/core/shapes/shapes-text.mjs +0 -13
- package/lib/core/social.mjs +0 -561
- package/lib/core/storage.mjs +0 -176
- package/lib/core/store.mjs +0 -578
- package/lib/core/wire-quotes.mjs +0 -71
- package/lib/core/wire.mjs +0 -925
- package/lib/device/account.mjs +0 -87
- package/lib/device/admin/index.mjs +0 -13
- package/lib/device/admin/origins.mjs +0 -35
- package/lib/device/admin/routes/connections.mjs +0 -144
- package/lib/device/admin/routes/gateway.mjs +0 -199
- package/lib/device/admin/routes/lifecycle.mjs +0 -200
- package/lib/device/admin/routes/owner.mjs +0 -322
- package/lib/device/admin/routes/setup.mjs +0 -428
- package/lib/device/admin/routes/social.mjs +0 -188
- package/lib/device/admin/server.mjs +0 -95
- package/lib/device/admin/static.mjs +0 -247
- package/lib/device/admin/surface.mjs +0 -315
- package/lib/device/certs.mjs +0 -281
- package/lib/device/cli/commands/account.mjs +0 -586
- package/lib/device/cli/commands/run.mjs +0 -278
- package/lib/device/cli/commands/service.mjs +0 -221
- package/lib/device/cli/commands/setup.mjs +0 -489
- package/lib/device/cli/commands/state.mjs +0 -561
- package/lib/device/cli/context.mjs +0 -288
- package/lib/device/export-collections.mjs +0 -161
- package/lib/device/gateway-move.mjs +0 -58
- package/lib/device/home.mjs +0 -161
- package/lib/device/migrate.mjs +0 -115
- package/lib/device/ports.mjs +0 -27
- package/lib/device/remote.mjs +0 -54
- package/lib/device/setup.mjs +0 -496
- package/lib/device/update.mjs +0 -96
- package/lib/gateway/caches.mjs +0 -36
- package/lib/gateway/directory.mjs +0 -90
- package/lib/gateway/front-core.mjs +0 -966
- package/lib/gateway/gateway-core.mjs +0 -244
- package/lib/gateway/headers.mjs +0 -49
- package/lib/gateway/httpsig.mjs +0 -159
- package/lib/gateway/notices.mjs +0 -84
- package/lib/gateway/quiet.mjs +0 -189
- package/lib/gateway/relay-extras.mjs +0 -89
- package/lib/gateway/token-claims.mjs +0 -16
- package/lib/pod/README.md +0 -61
- package/lib/pod/actor.mjs +0 -96
- package/lib/pod/collection.mjs +0 -77
- package/lib/pod/containers.mjs +0 -140
- package/lib/pod/discovery.mjs +0 -46
- package/lib/pod/featured.mjs +0 -18
- package/lib/pod/followers.mjs +0 -24
- package/lib/pod/following.mjs +0 -10
- package/lib/pod/http.mjs +0 -74
- package/lib/pod/inbox.mjs +0 -166
- package/lib/pod/links.mjs +0 -35
- package/lib/pod/location.mjs +0 -52
- package/lib/pod/media.mjs +0 -27
- package/lib/pod/notes.mjs +0 -78
- package/lib/pod/notifications.mjs +0 -89
- package/lib/pod/outbox.mjs +0 -18
- package/lib/pod/package.json +0 -19
- package/lib/pod/policy.mjs +0 -58
- package/lib/pod/private.mjs +0 -19
- package/lib/pod/root.mjs +0 -104
- package/lib/pod/state.mjs +0 -40
- package/lib/pod/transport.mjs +0 -674
- package/lib/pod/type-index.mjs +0 -101
- package/lib/pod/urls.mjs +0 -91
- package/lib/server/embed.mjs +0 -424
- package/lib/session/README.md +0 -137
- package/lib/session/demo.html +0 -94
- package/lib/session/fedi-account.mjs +0 -417
- package/lib/session/fedi-login.mjs +0 -123
- package/lib/session/oidc-session.mjs +0 -233
- package/lib/session/package.json +0 -49
- package/lib/shared/guard.mjs +0 -213
- package/lib/shared/links.mjs +0 -9
- package/lib/shared/safefetch.mjs +0 -235
- package/lib/shared/ua.mjs +0 -29
- package/phanpy/dist/404.html +0 -40
- package/phanpy/dist/_headers +0 -2
- package/phanpy/dist/apple-touch-icon.png +0 -0
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
- package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
- package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
- package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
- package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
- package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
- package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
- package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
- package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
- package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
- package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
- package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
- package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
- package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
- package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
- package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
- package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
- package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
- package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
- package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
- package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
- package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
- package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
- package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
- package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
- package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
- package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
- package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
- package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
- package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
- package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
- package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
- package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
- package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
- package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
- package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
- package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
- package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
- package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
- package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
- package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
- package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
- package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
- package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
- package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
- package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
- package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
- package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
- package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
- package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
- package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
- package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
- package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
- package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
- package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
- package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
- package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
- package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
- package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
- package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
- package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
- package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
- package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
- package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
- package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
- package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
- package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
- package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
- package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
- package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
- package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
- package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
- package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
- package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
- package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
- package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
- package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
- package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
- package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
- package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
- package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
- package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
- package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
- package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
- package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
- package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
- package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
- package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
- package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
- package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
- package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
- package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
- package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
- package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
- package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
- package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
- package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
- package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
- package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
- package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
- package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
- package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
- package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
- package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
- package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
- package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
- package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
- package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
- package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
- package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
- package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
- package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
- package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
- package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
- package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
- package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
- package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
- package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
- package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
- package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
- package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
- package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
- package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
- package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
- package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
- package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
- package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
- package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
- package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
- package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
- package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
- package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
- package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
- package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
- package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
- package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
- package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
- package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
- package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
- package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
- package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
- package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
- package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
- package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
- package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
- package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
- package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
- package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
- package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
- package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
- package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
- package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
- package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
- package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
- package/phanpy/dist/compose/index.html +0 -20
- package/phanpy/dist/favicon.ico +0 -0
- package/phanpy/dist/index.html +0 -73
- package/phanpy/dist/logo-192.png +0 -0
- package/phanpy/dist/logo-512.png +0 -0
- package/phanpy/dist/logo-badge-72.png +0 -0
- package/phanpy/dist/logo-maskable-512.png +0 -0
- package/phanpy/dist/logo-monochrome-512.png +0 -0
- package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
- package/phanpy/dist/manifest.webmanifest +0 -1
- package/phanpy/dist/og-image-2.jpg +0 -0
- package/phanpy/dist/og-image.png +0 -0
- package/phanpy/dist/robots.txt +0 -2
- package/phanpy/dist/sw.js +0 -2
- package/phanpy/dist/sw.js.map +0 -1
- package/phanpy/dist/version.json +0 -1
- package/run-agent.mjs +0 -899
- package/vendor/gate.cjs +0 -138
- package/vendor/idp-grant.cjs +0 -394
- package/vendor/jfetch.cjs +0 -16
- package/web/admin/actors.js +0 -147
- package/web/admin/bar.css +0 -126
- package/web/admin/bar.js +0 -99
- package/web/admin/client/client.js +0 -66
- package/web/admin/client/index.html +0 -69
- package/web/admin/client-pick.js +0 -79
- package/web/admin/common.js +0 -23
- package/web/admin/connections.js +0 -112
- package/web/admin/gateway.js +0 -133
- package/web/admin/group.js +0 -258
- package/web/admin/index.html +0 -612
- package/web/admin/notices-bar.js +0 -90
- package/web/admin/oauth-signin.mjs +0 -37
- package/web/admin/record.js +0 -393
- package/web/admin/setup/index.html +0 -257
- package/web/admin/setup/setup.js +0 -339
- package/web/admin/tokens.css +0 -68
- package/web/admin/upkeep.js +0 -177
- package/web/admin/window.css +0 -55
- package/web/admin/window.js +0 -136
- package/web/app/README.md +0 -81
- package/web/app/admin-facade.mjs +0 -588
- package/web/app/agent.mjs +0 -672
- package/web/app/atproto-browser.mjs +0 -86
- package/web/app/authorize_interaction.html +0 -56
- package/web/app/authorize_interaction.js +0 -69
- package/web/app/boot.mjs +0 -616
- package/web/app/deliver-relay.mjs +0 -129
- package/web/app/dist/boot.js +0 -34562
- package/web/app/dist/boot.js.map +0 -7
- package/web/app/dist/sw.js +0 -73456
- package/web/app/dist/sw.js.map +0 -7
- package/web/app/fediacct-browser.mjs +0 -146
- package/web/app/gateway-move.mjs +0 -60
- package/web/app/idb-kv.mjs +0 -35
- package/web/app/index.html +0 -258
- package/web/app/keys-browser.mjs +0 -71
- package/web/app/keystore.mjs +0 -82
- package/web/app/oidc-session.mjs +0 -6
- package/web/app/pod-auth.mjs +0 -248
- package/web/app/pod-remote.mjs +0 -84
- package/web/app/shims/_globals.mjs +0 -2
- package/web/app/shims/fedify-sig.mjs +0 -77
- package/web/app/shims/node-crypto.mjs +0 -113
- package/web/app/shims/node-fs.mjs +0 -17
- package/web/app/shims/node-path.mjs +0 -19
- package/web/app/shims/node-url.mjs +0 -7
- package/web/app/shims/prelude.js +0 -58
- package/web/app/shims/safefetch.mjs +0 -58
- package/web/app/shims/shapes-text.mjs +0 -8
- package/web/app/shims/web-push.mjs +0 -14
- package/web/app/signup.mjs +0 -357
- package/web/app/sw-src.mjs +0 -220
- package/web/app/update.js +0 -70
- package/web/front/admin.html +0 -55
- package/web/front/admin.js +0 -89
- package/web/front/install.sh +0 -69
- package/web/front/new-account.html +0 -35
- package/web/front/new-account.js +0 -13
- package/web/front/notices.html +0 -69
- package/web/front/notices.js +0 -107
- package/web/front/run.html +0 -64
- package/web/front/run.js +0 -221
- package/web/front/solid-oidc-client.js +0 -6
- package/web/icons/forum.svg +0 -11
- package/web/icons/gateway.svg +0 -14
- package/web/icons/local.svg +0 -11
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
// context.mjs — what every command starts from: the arguments, which
|
|
2
|
-
// identity (HOME) and port (PORT) a command means, and the helpers that ask,
|
|
3
|
-
// probe and finish. HOME, PORT and DEFAULT_ISSUE are live bindings: useProfile()
|
|
4
|
-
// reassigns them and every command module sees the new value.
|
|
5
|
-
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import { localFetch } from '../../../lib/client/localapi.mjs';
|
|
8
|
-
import os from 'node:os';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
import net from 'node:net';
|
|
11
|
-
import readline from 'node:readline';
|
|
12
|
-
import { spawn } from 'node:child_process';
|
|
13
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
14
|
-
import { apRoot, profilesDir, identityHomes, isLegacyRoot, CURRENT_ROOT, tildify, rootOf,
|
|
15
|
-
readRoot, writeRoot, defaultProfile, profileHome, rootHoldsIdentity, ROOT_FILE,
|
|
16
|
-
recordLastUsed, writeJsonAtomic } from '../../../lib/device/home.mjs';
|
|
17
|
-
import { insecureUrlReason } from '../../../lib/shared/safefetch.mjs';
|
|
18
|
-
import { portFree, freePortFrom } from '../../../lib/device/ports.mjs';
|
|
19
|
-
|
|
20
|
-
export const args = process.argv.slice(2);
|
|
21
|
-
export const cmd = args[0];
|
|
22
|
-
export const flag = (name, dflt) => {
|
|
23
|
-
const i = args.indexOf('--' + name);
|
|
24
|
-
return i >= 0 ? args[i + 1] : dflt;
|
|
25
|
-
};
|
|
26
|
-
export const has = (name) => args.includes('--' + name);
|
|
27
|
-
// The port can also be given bare — `npm start 8081` reaches us as `up 8081`,
|
|
28
|
-
// because npm passes positionals through but eats `--port`. Bare form only for
|
|
29
|
-
// the start-style commands, where a lone number cannot mean anything else.
|
|
30
|
-
export const barePort = () => {
|
|
31
|
-
if (!['up', 'start', 'run'].includes(cmd)) return null;
|
|
32
|
-
for (let i = 1; i < args.length; i++) {
|
|
33
|
-
if (/^\d+$/.test(args[i]) && !args[i - 1].startsWith('--')) return args[i];
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
36
|
-
};
|
|
37
|
-
export const portFlag = () => flag('port', null) || barePort();
|
|
38
|
-
// One identity per home, and EVERY identity is `<root>/profiles/<name>/`. There
|
|
39
|
-
// is no privileged unnamed one: `root.json` names which you get when you do not
|
|
40
|
-
// say, and that is a pointer you can change rather than a directory you have to
|
|
41
|
-
// move a private key out of. lib/home.mjs decides the root.
|
|
42
|
-
//
|
|
43
|
-
// An explicit --home / AP_HOME still wins and is taken literally: you named a
|
|
44
|
-
// directory, so that directory is the identity, root.json unread. That is what
|
|
45
|
-
// `rootOf` has always documented for a custom home.
|
|
46
|
-
export const PROFILE = flag('profile', process.env.AP_PROFILE || null);
|
|
47
|
-
export const AP_ROOT = apRoot();
|
|
48
|
-
export const PROFILES_DIR = profilesDir(AP_ROOT);
|
|
49
|
-
|
|
50
|
-
// `let`, because setup does not know which identity it is until it has asked for
|
|
51
|
-
// the handle — the home is named after it. Everything below reads HOME at call
|
|
52
|
-
// time, so reassigning it once, early, is enough; PORT is the exception and is
|
|
53
|
-
// recomputed with it.
|
|
54
|
-
export let DEFAULT_ISSUE = null; // set when the pointer is unusable
|
|
55
|
-
export let HOME = flag('home', process.env.AP_HOME || (() => {
|
|
56
|
-
if (PROFILE) return profileHome(AP_ROOT, PROFILE);
|
|
57
|
-
const d = defaultProfile(AP_ROOT);
|
|
58
|
-
if (typeof d === 'string') return profileHome(AP_ROOT, d);
|
|
59
|
-
DEFAULT_ISSUE = d?.missing
|
|
60
|
-
? `${ROOT_FILE} names "${d.missing}", which is not an identity here`
|
|
61
|
-
: 'there is more than one identity here and none is the default';
|
|
62
|
-
return profileHome(AP_ROOT, d?.missing || '');
|
|
63
|
-
})());
|
|
64
|
-
|
|
65
|
-
// The port chosen at setup is remembered, so `start`/`stop`/`status` need no
|
|
66
|
-
// flags afterwards. Precedence: --port (or a bare port number, `npm start
|
|
67
|
-
// 8081`) > AP_PORT > the recorded choice > 8030.
|
|
68
|
-
// The handle is remembered alongside it, for the named origin: the agent also
|
|
69
|
-
// answers at <handle>.localhost:<port>, and that has to work from the first
|
|
70
|
-
// request, before pod state has been read.
|
|
71
|
-
export function recordedAgent() {
|
|
72
|
-
try { return JSON.parse(fs.readFileSync(path.join(HOME, 'agent.json'), 'utf8')) || {}; }
|
|
73
|
-
catch { return {}; }
|
|
74
|
-
}
|
|
75
|
-
export function recordedPort() { return Number(recordedAgent().port) || null; }
|
|
76
|
-
export function recordAgent(fields) {
|
|
77
|
-
try {
|
|
78
|
-
fs.mkdirSync(HOME, { recursive: true, mode: 0o700 });
|
|
79
|
-
const rec = { ...recordedAgent(), ...fields };
|
|
80
|
-
writeJsonAtomic(path.join(HOME, 'agent.json'), rec, { mode: 0o644 });
|
|
81
|
-
} catch { /* the flag still works, it just isn't remembered */ }
|
|
82
|
-
}
|
|
83
|
-
export let PORT = Number(portFlag() || process.env.AP_PORT || recordedPort() || 8030);
|
|
84
|
-
|
|
85
|
-
// Setup is the one command that cannot know its home in advance: the identity is
|
|
86
|
-
// named after the handle, and the handle is the first thing it asks. Everything
|
|
87
|
-
// that reads HOME does so at call time, so pointing it at the right directory as
|
|
88
|
-
// soon as the name exists is enough — PORT is recomputed because it was read
|
|
89
|
-
// from the old home's agent.json.
|
|
90
|
-
export function useProfile(name) {
|
|
91
|
-
HOME = flag('home', process.env.AP_HOME || profileHome(AP_ROOT, name));
|
|
92
|
-
PORT = Number(portFlag() || process.env.AP_PORT || recordedPort() || 8030);
|
|
93
|
-
DEFAULT_ISSUE = null;
|
|
94
|
-
return HOME;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// A handle becomes a directory name, so it is checked before it is one. Same
|
|
98
|
-
// rule the admin API applies before creating an actor (admin.mjs) — without it
|
|
99
|
-
// a handle containing a slash or `..` climbs out of profiles/.
|
|
100
|
-
const HANDLE_RE = /^[a-z0-9][a-z0-9_-]{0,30}$/;
|
|
101
|
-
export function requireHandle(handle) {
|
|
102
|
-
if (HANDLE_RE.test(handle)) return handle;
|
|
103
|
-
console.error(`"${handle}" cannot be a handle: letters, digits, hyphens and underscores,`);
|
|
104
|
-
console.error('starting with a letter or digit, at most 31 characters.');
|
|
105
|
-
process.exit(2);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Commands that act on an identity need one decided. Says which of the three
|
|
109
|
-
// ways it failed, because "no identity" and "which identity" are different
|
|
110
|
-
// problems with different fixes.
|
|
111
|
-
export function requireIdentity() {
|
|
112
|
-
// An explicit AP_HOME / --home is an explicit identity directory. Nothing
|
|
113
|
-
// about the machine's root applies to it — including whether that root has
|
|
114
|
-
// been restructured, which is somebody else's install's problem.
|
|
115
|
-
if (process.env.AP_HOME || flag('home')) return;
|
|
116
|
-
if (rootHoldsIdentity(AP_ROOT)) {
|
|
117
|
-
console.error(`${tildify(AP_ROOT)} still keeps an identity at its top level.`);
|
|
118
|
-
console.error('Every identity lives in profiles/<name>/ now. Move this one down with:\n');
|
|
119
|
-
console.error(` ${process.argv[1]} home --restructure\n`);
|
|
120
|
-
process.exit(2);
|
|
121
|
-
}
|
|
122
|
-
if (!DEFAULT_ISSUE) return;
|
|
123
|
-
const homes = identityHomes(AP_ROOT).filter(h => fs.existsSync(path.join(h.dir, 'credential.json')));
|
|
124
|
-
console.error(DEFAULT_ISSUE + '.');
|
|
125
|
-
if (homes.length) {
|
|
126
|
-
console.error(`\n ${process.argv[1]} --profile <name> start\n`);
|
|
127
|
-
console.error('Whichever you start is remembered, so plain commands mean that one afterwards.');
|
|
128
|
-
console.error(`here: ${homes.map(h => h.name).join(', ')}`);
|
|
129
|
-
} else {
|
|
130
|
-
console.error(`\nThere are no identities yet — ${process.argv[1]} setup`);
|
|
131
|
-
}
|
|
132
|
-
process.exit(2);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function askHidden(prompt) {
|
|
136
|
-
return new Promise((resolve) => {
|
|
137
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
138
|
-
const onData = (c) => { if (String(c) !== '\n' && String(c) !== '\r') readline.moveCursor(process.stdout, -1, 0), process.stdout.write('*'); };
|
|
139
|
-
process.stdin.on('data', onData);
|
|
140
|
-
rl.question(prompt, (answer) => { process.stdin.off('data', onData); rl.close(); process.stdout.write('\n'); resolve(answer); });
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Plain prompt with a default: Enter accepts it. Non-interactive runs
|
|
145
|
-
// (scripts, CI) take the default silently, so flags remain sufficient.
|
|
146
|
-
let sharedRl = null; // one interface: a new one per
|
|
147
|
-
export function ask(prompt, dflt = '') { // question would drop buffered input
|
|
148
|
-
if (!process.stdin.isTTY) return Promise.resolve(dflt);
|
|
149
|
-
sharedRl ||= readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
150
|
-
return new Promise((resolve) => {
|
|
151
|
-
sharedRl.question(dflt ? `${prompt} [${dflt}]: ` : `${prompt}: `, (answer) => {
|
|
152
|
-
resolve(String(answer).trim() || dflt);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
export function endAsking() { sharedRl?.close(); sharedRl = null; }
|
|
157
|
-
|
|
158
|
-
// "Occupied" means "cannot be bound", not "does not answer HTTP": something
|
|
159
|
-
// holding a port without speaking HTTP reads as free to a GET, and then the
|
|
160
|
-
// agent dies on EADDRINUSE.
|
|
161
|
-
|
|
162
|
-
// The first port from `first` upward that binds. Walking always ends in one,
|
|
163
|
-
// so this is a step rather than a condition.
|
|
164
|
-
|
|
165
|
-
// Is anything at all on this port? For the operations that MOVE data, "it did
|
|
166
|
-
// not answer as one of ours" is not the same as "nothing is there": an agent
|
|
167
|
-
// started with AP_GATE_TOKEN answers 401 to an un-tokened /status, so agentOn
|
|
168
|
-
// reads a perfectly live agent as stopped — and a sweep that trusted it would
|
|
169
|
-
// copy the state out from under one, which the next write then overwrites.
|
|
170
|
-
// Bind to find out, and fail closed.
|
|
171
|
-
export async function somethingOn(port) {
|
|
172
|
-
const mine = await agentOn(port);
|
|
173
|
-
if (mine) return 'running';
|
|
174
|
-
return (await portFree(port)) ? null : 'something is on the port and did not answer as ours';
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Whatever is on the port — is it one of ours?
|
|
178
|
-
export async function agentOn(port) {
|
|
179
|
-
try {
|
|
180
|
-
const res = await localFetch(HOME, port, `/status`, { signal: AbortSignal.timeout(2000) });
|
|
181
|
-
const body = await res.json();
|
|
182
|
-
return typeof body?.configured === 'boolean' ? body : null;
|
|
183
|
-
} catch { return null; }
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export const isInside = (root, p) => {
|
|
187
|
-
const rel = path.relative(root, p);
|
|
188
|
-
return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
export function openBrowser(url) {
|
|
192
|
-
try {
|
|
193
|
-
const win = process.platform === 'win32';
|
|
194
|
-
const cmd = win ? 'cmd' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
195
|
-
const child = spawn(cmd, win ? ['/c', 'start', '', url] : [url], { detached: true, stdio: 'ignore' });
|
|
196
|
-
// A box with no opener at all (a server, a bare container) emits this
|
|
197
|
-
// asynchronously, where the try/catch cannot reach it — and an unhandled
|
|
198
|
-
// 'error' event on a child process ends the agent.
|
|
199
|
-
child.on('error', () => {});
|
|
200
|
-
child.unref();
|
|
201
|
-
} catch { /* best-effort — the URL is printed anyway */ }
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// A one-shot command holds the lease only for its own duration: exiting without
|
|
205
|
-
// releasing it leaves the next `start` as a read-only viewer until the lease
|
|
206
|
-
// expires, which is 5 minutes of doing nothing.
|
|
207
|
-
export async function finish(agent, code = 0) {
|
|
208
|
-
await agent.lease?.release().catch(() => {});
|
|
209
|
-
await agent.store?.flush?.().catch(() => {});
|
|
210
|
-
process.exit(code);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Anything that decides the identity. Given even one of these, setup stays on
|
|
214
|
-
// the command line exactly as it always did — scripts, CI and the tarball's
|
|
215
|
-
// unpack-and-go line depend on that. At a terminal with none of them, setup
|
|
216
|
-
// asks the two things it needs to open a browser and asks the rest there.
|
|
217
|
-
export const IDENTITY_FLAGS = ['new-account', 'pod', 'issuer', 'email', 'name', 'pod-name',
|
|
218
|
-
'group', 'approve-joins', 'summary', 'icon', 'root', 'in', 'create-type-index', 'keys', 'rotate-key'];
|
|
219
|
-
|
|
220
|
-
// Refuse before anything is asked, let alone typed: setup used to overwrite
|
|
221
|
-
// credential.json in place, and a minted credential is only shown once — so
|
|
222
|
-
// the identity it belonged to could not be recovered afterwards.
|
|
223
|
-
export function refuseExistingIdentity() {
|
|
224
|
-
const credPath = path.join(HOME, 'credential.json');
|
|
225
|
-
if (!fs.existsSync(credPath) || has('force')) return;
|
|
226
|
-
let held = '(unreadable)';
|
|
227
|
-
try { held = JSON.parse(fs.readFileSync(credPath, 'utf8')).remotePod; } catch {}
|
|
228
|
-
console.error(`${HOME} already holds an identity: ${held}`);
|
|
229
|
-
console.error('For another identity: fedipod setup --profile <name>');
|
|
230
|
-
console.error('To list what exists: fedipod profiles');
|
|
231
|
-
console.error('To replace this one: add --force (the old credential is lost)');
|
|
232
|
-
console.error('');
|
|
233
|
-
console.error('If a setup died half-way, do NOT re-run it — the credential it already');
|
|
234
|
-
console.error('minted cannot be minted twice. Run `fedipod start` and');
|
|
235
|
-
console.error('finish at /admin/setup/ in the browser.');
|
|
236
|
-
process.exit(2);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Ask the handle (permanent, and it names the origin) and the port, start
|
|
240
|
-
// serving, and hand over to the page at /admin/setup/. Nothing is created here: the
|
|
241
|
-
// agent's own POST /setup does all of it, so a closed tab cannot lose a
|
|
242
|
-
// credential that only exists in an HTTP response.
|
|
243
|
-
export async function runBrowserSetup() {
|
|
244
|
-
const handle = flag('handle') || await ask('handle (the name in your address; permanent)');
|
|
245
|
-
if (!handle) { console.error('a handle is required'); process.exit(2); }
|
|
246
|
-
// The handle names the home, so nothing can be decided before it — including
|
|
247
|
-
// which identity would be overwritten, and which port was remembered.
|
|
248
|
-
requireHandle(handle);
|
|
249
|
-
useProfile(PROFILE || handle);
|
|
250
|
-
refuseExistingIdentity();
|
|
251
|
-
const port = portFlag() ? PORT : (Number(await ask('port', String(PORT))) || PORT);
|
|
252
|
-
endAsking();
|
|
253
|
-
|
|
254
|
-
// Recorded before the server starts, so `stop`/`status` work while the
|
|
255
|
-
// browser flow is still open — it used to be written only after the mint.
|
|
256
|
-
recordAgent({ port, handle });
|
|
257
|
-
|
|
258
|
-
const { Agent } = await import(new URL('../../../run-agent.mjs', import.meta.url));
|
|
259
|
-
const { startAdmin } = await import(new URL('../../../lib/device/admin/index.mjs', import.meta.url));
|
|
260
|
-
const { hostLabel } = await import(new URL('../../../lib/shared/guard.mjs', import.meta.url));
|
|
261
|
-
const agent = new Agent({ home: HOME, log: (...a) => console.log('[ap]', ...a) });
|
|
262
|
-
startAdmin({
|
|
263
|
-
port, handle, agent,
|
|
264
|
-
gateToken: process.env.AP_GATE_TOKEN || '',
|
|
265
|
-
log: (...a) => console.log('[ap]', ...a),
|
|
266
|
-
});
|
|
267
|
-
const shutdown = () => {
|
|
268
|
-
setTimeout(() => process.exit(0), 1500).unref();
|
|
269
|
-
try { fs.rmSync(path.join(HOME, 'agent.pid'), { force: true }); } catch {}
|
|
270
|
-
Promise.allSettled([agent.store.flush(), agent.lease?.release()]).finally(() => process.exit(0));
|
|
271
|
-
};
|
|
272
|
-
process.on('SIGINT', shutdown);
|
|
273
|
-
process.on('SIGTERM', shutdown);
|
|
274
|
-
|
|
275
|
-
const label = hostLabel(handle);
|
|
276
|
-
const named = label ? `https://${label}.localhost:${port}/` : null;
|
|
277
|
-
const plain = `https://localhost:${port}/`;
|
|
278
|
-
const pad = Math.max(named?.length || 0, plain.length);
|
|
279
|
-
console.log('');
|
|
280
|
-
if (named) {
|
|
281
|
-
console.log(` ${named.padEnd(pad)} <- opening this`);
|
|
282
|
-
console.log(` ${plain.padEnd(pad)} <- the same agent, if your browser cannot find that name`);
|
|
283
|
-
} else {
|
|
284
|
-
console.log(` ${plain}`);
|
|
285
|
-
}
|
|
286
|
-
console.log('\nsetup continues in the browser — Ctrl-C to stop\n');
|
|
287
|
-
openBrowser(named || plain);
|
|
288
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
// export-collections.mjs — the account's collections as paged Turtle AS2
|
|
2
|
-
// collections: `as:Collection` / `as:OrderedCollectionPage` documents whose
|
|
3
|
-
// pages carry `as:items` arcs plus the activity's own triples. Produced ON
|
|
4
|
-
// DEMAND — for a migration, or for any AS2/RDF reader — so the live pod pays
|
|
5
|
-
// nothing to keep this shape fresh. The layout matches the one kept by
|
|
6
|
-
// https://github.com/jg10-mastodon-social/solid-activitypub-netlify.
|
|
7
|
-
//
|
|
8
|
-
// Fidelity: their skeleton plus AS2-subset activity triples (type, actor,
|
|
9
|
-
// object, published, to/cc, content, inReplyTo, attachment). Media blobs,
|
|
10
|
-
// DMs, pins and standalone note documents have no place in that layout and
|
|
11
|
-
// are not carried.
|
|
12
|
-
//
|
|
13
|
-
// Serialised by rdflib, never by hand.
|
|
14
|
-
|
|
15
|
-
import * as $rdf from 'rdflib';
|
|
16
|
-
|
|
17
|
-
const AS = $rdf.Namespace('https://www.w3.org/ns/activitystreams#');
|
|
18
|
-
const RDF = $rdf.Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
|
|
19
|
-
const XSD = $rdf.Namespace('http://www.w3.org/2001/XMLSchema#');
|
|
20
|
-
const TURTLE = 'text/turtle';
|
|
21
|
-
const PAGE_SIZE = 20;
|
|
22
|
-
|
|
23
|
-
// Doc IRIs need a base even when the target is a directory; links between the
|
|
24
|
-
// export's own documents relativise against it, so the files stay portable.
|
|
25
|
-
export const DIR_BASE = 'https://fedipod-export.invalid/';
|
|
26
|
-
|
|
27
|
-
const sym = (v) => { try { return typeof v === 'string' && /^[a-z][a-z0-9+.-]*:/i.test(v) ? $rdf.sym(v) : null; } catch { return null; } };
|
|
28
|
-
const typeNode = (t) => (typeof t === 'string' && /^[A-Za-z]+$/.test(t) ? AS(t) : null);
|
|
29
|
-
|
|
30
|
-
// The object-ish AS2-subset fields, shared by activities, embedded objects,
|
|
31
|
-
// and bare Notes an outbox names by IRI.
|
|
32
|
-
function addObjectFields(g, doc, s, o) {
|
|
33
|
-
const t = typeNode(o.type);
|
|
34
|
-
if (t) g.add(s, RDF('type'), t, doc);
|
|
35
|
-
const by = sym(typeof o.attributedTo === 'string' ? o.attributedTo : o.attributedTo?.id);
|
|
36
|
-
if (by) g.add(s, AS('attributedTo'), by, doc);
|
|
37
|
-
if (o.published) g.add(s, AS('published'), $rdf.literal(o.published, XSD('dateTime')), doc);
|
|
38
|
-
if (typeof o.content === 'string') g.add(s, AS('content'), $rdf.literal(o.content), doc);
|
|
39
|
-
const irt = sym(typeof o.inReplyTo === 'string' ? o.inReplyTo : o.inReplyTo?.id);
|
|
40
|
-
if (irt) g.add(s, AS('inReplyTo'), irt, doc);
|
|
41
|
-
for (const at of [].concat(o.attachment || [])) {
|
|
42
|
-
const au = sym(at?.url);
|
|
43
|
-
if (!au) continue;
|
|
44
|
-
g.add(s, AS('attachment'), au, doc);
|
|
45
|
-
g.add(au, RDF('type'), AS('Document'), doc);
|
|
46
|
-
if (at.mediaType) g.add(au, AS('mediaType'), $rdf.literal(at.mediaType), doc);
|
|
47
|
-
if (at.name) g.add(au, AS('name'), $rdf.literal(at.name), doc);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// One activity's (or bare object's) AS2-subset triples into the page document.
|
|
52
|
-
function addActivity(g, doc, a) {
|
|
53
|
-
const s = sym(a.id);
|
|
54
|
-
if (!s) return false;
|
|
55
|
-
addObjectFields(g, doc, s, a);
|
|
56
|
-
const actor = sym(typeof a.actor === 'string' ? a.actor : a.actor?.id);
|
|
57
|
-
if (actor) g.add(s, AS('actor'), actor, doc);
|
|
58
|
-
for (const key of ['to', 'cc']) {
|
|
59
|
-
for (const addr of [].concat(a[key] || [])) {
|
|
60
|
-
const n = sym(addr);
|
|
61
|
-
if (n) g.add(s, AS(key), n, doc);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const o = a.object;
|
|
65
|
-
if (typeof o === 'string') {
|
|
66
|
-
const n = sym(o);
|
|
67
|
-
if (n) g.add(s, AS('object'), n, doc);
|
|
68
|
-
} else if (o && typeof o === 'object' && sym(o.id)) {
|
|
69
|
-
const n = sym(o.id);
|
|
70
|
-
g.add(s, AS('object'), n, doc);
|
|
71
|
-
addObjectFields(g, doc, n, o);
|
|
72
|
-
}
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// A collection into documents: a head at <name>/collection.ttl pointing
|
|
77
|
-
// as:first at the newest page, pages at <name>/pages/<slot>.ttl chained
|
|
78
|
-
// as:next toward the older ones — the direction their layout walks. Items:
|
|
79
|
-
// activity objects (triples added) or bare IRIs (followers).
|
|
80
|
-
export function collectionDocs(name, items, base) {
|
|
81
|
-
const headUrl = `${base}${name}/collection.ttl`;
|
|
82
|
-
const slots = [];
|
|
83
|
-
const seen = new Set();
|
|
84
|
-
const pages = [];
|
|
85
|
-
for (let i = 0; i < items.length; i += PAGE_SIZE) pages.push(items.slice(i, i + PAGE_SIZE));
|
|
86
|
-
if (!pages.length) pages.push([]);
|
|
87
|
-
for (const page of pages) {
|
|
88
|
-
const first = page.find(it => typeof it === 'object' && it?.published);
|
|
89
|
-
let slot = String(first ? Date.parse(first.published) || 0 : 0);
|
|
90
|
-
while (seen.has(slot)) slot += 'x';
|
|
91
|
-
seen.add(slot);
|
|
92
|
-
slots.push(slot);
|
|
93
|
-
}
|
|
94
|
-
const pageUrl = (i) => `${base}${name}/pages/${slots[i]}.ttl`;
|
|
95
|
-
const docs = [];
|
|
96
|
-
|
|
97
|
-
const hg = $rdf.graph();
|
|
98
|
-
const head = $rdf.sym(headUrl);
|
|
99
|
-
hg.add(head, RDF('type'), AS('Collection'), head);
|
|
100
|
-
hg.add(head, AS('first'), $rdf.sym(pageUrl(0)), head);
|
|
101
|
-
docs.push({ path: `${name}/collection.ttl`, turtle: $rdf.serialize(head, hg, headUrl, TURTLE) });
|
|
102
|
-
|
|
103
|
-
let carried = 0;
|
|
104
|
-
pages.forEach((page, i) => {
|
|
105
|
-
const url = pageUrl(i);
|
|
106
|
-
const g = $rdf.graph();
|
|
107
|
-
const doc = $rdf.sym(url);
|
|
108
|
-
g.add(doc, RDF('type'), AS('OrderedCollectionPage'), doc);
|
|
109
|
-
if (i + 1 < pages.length) g.add(doc, AS('next'), $rdf.sym(pageUrl(i + 1)), doc);
|
|
110
|
-
for (const item of page) {
|
|
111
|
-
if (typeof item === 'string') {
|
|
112
|
-
const n = sym(item);
|
|
113
|
-
if (n) { g.add(doc, AS('items'), n, doc); carried++; }
|
|
114
|
-
} else if (item && addActivity(g, doc, item)) {
|
|
115
|
-
g.add(doc, AS('items'), $rdf.sym(item.id), doc);
|
|
116
|
-
carried++;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
docs.push({ path: `${name}/pages/${slots[i]}.ttl`, turtle: $rdf.serialize(doc, g, url, TURTLE) });
|
|
120
|
-
});
|
|
121
|
-
return { docs, carried, pages: pages.length };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// The whole export. Inputs arrive as plain data so the CLI and the tests
|
|
125
|
-
// assemble them the same way; `storage` is a lib/storage.mjs container.
|
|
126
|
-
export async function exportCollections({ outboxItems = [], followers = [], inboxEntries = [], storage, base = DIR_BASE, log = console.log, resolve = null }) {
|
|
127
|
-
const out = { written: 0 };
|
|
128
|
-
// A published outbox may hold bare note IRIs rather than embedded
|
|
129
|
-
// activities; with a resolver those are fetched once each so their triples
|
|
130
|
-
// travel in the page instead of a pointer to a pod that may be retiring.
|
|
131
|
-
if (resolve) {
|
|
132
|
-
const resolved = [];
|
|
133
|
-
for (const it of outboxItems) {
|
|
134
|
-
if (typeof it !== 'string') { resolved.push(it); continue; }
|
|
135
|
-
const doc = await resolve(it).catch(() => null);
|
|
136
|
-
resolved.push(doc && doc.id ? doc : it);
|
|
137
|
-
}
|
|
138
|
-
outboxItems = resolved;
|
|
139
|
-
}
|
|
140
|
-
const write = async (name, items) => {
|
|
141
|
-
const { docs, carried, pages } = collectionDocs(name, items, base);
|
|
142
|
-
for (const d of docs) {
|
|
143
|
-
const w = await storage.write(d.path, d.turtle, TURTLE);
|
|
144
|
-
if (!w.ok) throw new Error(`write ${d.path}: ${w.why || 'failed'}`);
|
|
145
|
-
out.written++;
|
|
146
|
-
}
|
|
147
|
-
out[name] = { items: carried, pages };
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
await write('outbox', outboxItems);
|
|
151
|
-
await write('followers', followers);
|
|
152
|
-
// Newest first, like the outbox — receivedAt rides in each archive entry.
|
|
153
|
-
const inbox = [...inboxEntries]
|
|
154
|
-
.sort((a, b) => String(b.receivedAt || '').localeCompare(String(a.receivedAt || '')))
|
|
155
|
-
.map(e => { try { return { published: e.receivedAt, ...JSON.parse(e.raw) }; } catch { return null; } })
|
|
156
|
-
.filter(Boolean);
|
|
157
|
-
await write('inbox', inbox);
|
|
158
|
-
if (!inbox.length) log('inbox: no archived mail yet — history begins when the archive does');
|
|
159
|
-
log('carries what the collection layout can hold — media blobs, DMs, pins and standalone notes are not representable and were not exported');
|
|
160
|
-
return out;
|
|
161
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// gateway-move.mjs — the second half of a DeviceAgent moving an address from
|
|
2
|
-
// one gateway to another (lib/device/setup.mjs does the first, when a pod
|
|
3
|
-
// that already holds an account at another gateway is set up with an address
|
|
4
|
-
// at this one). The browser build has the same two halves in
|
|
5
|
-
// web/app/gateway-move.mjs; this is the Node one, which signs and delivers
|
|
6
|
-
// itself instead of going through a relay.
|
|
7
|
-
//
|
|
8
|
-
// 1. The old gateway is told the address moved (POST /api/move, proved with
|
|
9
|
-
// the pod session as attach was). From then on it serves the old actor
|
|
10
|
-
// as a moved stub — same key, old id, `movedTo` the new one.
|
|
11
|
-
// 2. A Move goes to every follower, FROM the old actor, signed under the
|
|
12
|
-
// old key id: the only signature a follower's server can verify against
|
|
13
|
-
// the old actor. Same key material; only the key id differs.
|
|
14
|
-
//
|
|
15
|
-
// Runs when the agent starts acting, and is safe to run again: a move left
|
|
16
|
-
// pending (the old gateway unreachable) is tried at the next start.
|
|
17
|
-
import { Deliverer } from '../core/deliver.mjs';
|
|
18
|
-
import { moveActivity } from '../core/wire.mjs';
|
|
19
|
-
|
|
20
|
-
export async function completeGatewayMove(agent, { makeDeliverer = (o) => new Deliverer(o) } = {}) {
|
|
21
|
-
const cfg = agent.store.getConfig();
|
|
22
|
-
const mv = cfg?.movedFrom;
|
|
23
|
-
if (!mv || mv.completedAt) return null;
|
|
24
|
-
const log = agent.log || (() => {});
|
|
25
|
-
const newActor = agent.urls.actor;
|
|
26
|
-
|
|
27
|
-
// 1. the old gateway
|
|
28
|
-
let told;
|
|
29
|
-
try {
|
|
30
|
-
told = await agent.remote.session.fetch(`${mv.gateway}/api/move`, {
|
|
31
|
-
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
32
|
-
body: JSON.stringify({ handle: mv.handle, movedTo: newActor }),
|
|
33
|
-
});
|
|
34
|
-
} catch (e) { told = { status: 0, statusText: e.message }; }
|
|
35
|
-
if (told.status !== 200) {
|
|
36
|
-
log(`gateway move: ${mv.gateway} did not mark @${mv.handle} as moved (${told.status || told.statusText}) — will retry at the next start`);
|
|
37
|
-
return { told: false };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 2. the Move, from the old address
|
|
41
|
-
const contacts = agent.store.getContacts();
|
|
42
|
-
const inboxes = [...new Set(contacts.followers.map((f) => f.sharedInbox || f.inbox).filter(Boolean))];
|
|
43
|
-
const activity = moveActivity({ actor: mv.actor }, newActor, Date.now());
|
|
44
|
-
const sender = makeDeliverer({
|
|
45
|
-
store: agent.store, rsaPrivate: agent.deliverer.rsaPrivate,
|
|
46
|
-
keyId: `${mv.actor}#main-key`, actorId: mv.actor, log, passive: true,
|
|
47
|
-
});
|
|
48
|
-
let sent = 0; let failed = 0;
|
|
49
|
-
for (const inbox of inboxes) {
|
|
50
|
-
try { await sender.deliverNow(inbox, activity); sent++; }
|
|
51
|
-
catch (e) { failed++; log(`gateway move: Move to ${inbox} failed: ${e.message}`); }
|
|
52
|
-
}
|
|
53
|
-
const done = { ...mv, completedAt: new Date().toISOString(), moveSent: sent, moveFailed: failed };
|
|
54
|
-
agent.store.setConfig({ ...agent.store.getConfig(), movedFrom: done });
|
|
55
|
-
await agent.store.flush?.();
|
|
56
|
-
log(`moved from ${mv.actor} to ${newActor}: Move sent to ${sent} inbox(es)${failed ? `, ${failed} failed` : ''}`);
|
|
57
|
-
return done;
|
|
58
|
-
}
|
package/lib/device/home.mjs
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
// Where an install keeps its credential, signing keys, pidfile and log.
|
|
2
|
-
//
|
|
3
|
-
// The root was `~/.activitypod`, then `~/.solid-activitypub`, before the
|
|
4
|
-
// rename to fedipod. An install that already has one keeps using it: that
|
|
5
|
-
// directory holds the credential and the private key, and moving those is the
|
|
6
|
-
// owner's decision rather than an upgrade's side effect. `fedipod home
|
|
7
|
-
// --to <dir>` is how you take the new name when you want it.
|
|
8
|
-
//
|
|
9
|
-
// Resolve ONCE and derive `profiles/` from the same answer. Deciding the two
|
|
10
|
-
// separately is how `--profile solo` ends up looking in a different tree from
|
|
11
|
-
// the default agent.
|
|
12
|
-
|
|
13
|
-
import fs from 'node:fs';
|
|
14
|
-
import os from 'node:os';
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import { fileURLToPath } from 'node:url';
|
|
17
|
-
|
|
18
|
-
export const CURRENT_ROOT = '.fedipod';
|
|
19
|
-
export const LEGACY_ROOTS = ['.solid-activitypub', '.activitypod']; // most recent first
|
|
20
|
-
|
|
21
|
-
// Existing install wins over the new name; a fresh one gets the new name.
|
|
22
|
-
export function apRoot(homedir = os.homedir()) {
|
|
23
|
-
const current = path.join(homedir, CURRENT_ROOT);
|
|
24
|
-
if (fs.existsSync(current)) return current;
|
|
25
|
-
for (const name of LEGACY_ROOTS) {
|
|
26
|
-
const legacy = path.join(homedir, name);
|
|
27
|
-
if (fs.existsSync(legacy)) return legacy;
|
|
28
|
-
}
|
|
29
|
-
return current;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const isLegacyRoot = (root) => LEGACY_ROOTS.includes(path.basename(root));
|
|
33
|
-
|
|
34
|
-
// Write one of this home's files without a window in which it is half there.
|
|
35
|
-
//
|
|
36
|
-
// lib/storage.mjs goes to the trouble of tmp+rename for state documents that
|
|
37
|
-
// its own header calls rebuildable from the pod. The files here are the ones
|
|
38
|
-
// that are NOT: keys.json is the private key remote servers have cached, and
|
|
39
|
-
// credential.json holds a secret a Solid server mints once and will not mint
|
|
40
|
-
// again. A plain writeFileSync truncates first, so a crash, a power loss or a
|
|
41
|
-
// full disk between truncate and write leaves an empty or half-written file
|
|
42
|
-
// where the identity used to be.
|
|
43
|
-
//
|
|
44
|
-
// rename(2) is atomic on POSIX, so a reader — or a backup running at the wrong
|
|
45
|
-
// moment — sees either the old file or the new one. fsync before it, so the
|
|
46
|
-
// content is on the platter and not merely in the page cache when the rename
|
|
47
|
-
// lands. Windows has no atomic rename over an existing file; there the copy
|
|
48
|
-
// path is still better than a truncate, and this is a best effort by design.
|
|
49
|
-
export function writeFileAtomic(file, body, { mode = 0o600 } = {}) {
|
|
50
|
-
const tmp = `${file}.${process.pid}.tmp`;
|
|
51
|
-
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
52
|
-
const fd = fs.openSync(tmp, 'w', mode);
|
|
53
|
-
try {
|
|
54
|
-
fs.writeFileSync(fd, body);
|
|
55
|
-
fs.fsyncSync(fd);
|
|
56
|
-
} finally {
|
|
57
|
-
fs.closeSync(fd);
|
|
58
|
-
}
|
|
59
|
-
fs.renameSync(tmp, file);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// The same, for the JSON these files all happen to be.
|
|
63
|
-
export function writeJsonAtomic(file, obj, opts) {
|
|
64
|
-
writeFileAtomic(file, JSON.stringify(obj, null, 2) + '\n', opts);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const profilesDir = (root) => path.join(root, 'profiles');
|
|
68
|
-
|
|
69
|
-
// The root an arbitrary home belongs to. `<root>/profiles/<name>` is a profile;
|
|
70
|
-
// anything else is its own root — so a custom AP_HOME reports no neighbours,
|
|
71
|
-
// which is the truth rather than a special case.
|
|
72
|
-
export function rootOf(home) {
|
|
73
|
-
const resolved = path.resolve(home);
|
|
74
|
-
const parent = path.dirname(resolved);
|
|
75
|
-
return path.basename(parent) === 'profiles' ? path.dirname(parent) : resolved;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Every identity under a root — all of them under `profiles/`, none of them the
|
|
79
|
-
// root itself. Callers read `agent.json` from these — a port and a handle.
|
|
80
|
-
// Nothing here, and nothing that uses it, opens a sibling's credential or keys.
|
|
81
|
-
//
|
|
82
|
-
// The root used to BE one of these, and had no name, so this function invented
|
|
83
|
-
// `(default)` for it at display time. That was the tell: a thing you have to
|
|
84
|
-
// name when you show it does not have an identity, it has a position. It also
|
|
85
|
-
// meant one identity's home contained all the others — back it up and you
|
|
86
|
-
// backed up everyone; delete it and you deleted everyone.
|
|
87
|
-
export function identityHomes(root) {
|
|
88
|
-
const homes = [];
|
|
89
|
-
try {
|
|
90
|
-
for (const name of fs.readdirSync(profilesDir(root)).sort()) {
|
|
91
|
-
const dir = path.join(profilesDir(root), name);
|
|
92
|
-
if (fs.statSync(dir).isDirectory()) homes.push({ name, dir });
|
|
93
|
-
}
|
|
94
|
-
} catch { /* no identities yet */ }
|
|
95
|
-
return homes;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// The root's own record. Today it holds one thing: which identity was last
|
|
99
|
-
// started, which is what a plain command means afterwards. Nobody sets it —
|
|
100
|
-
// using an identity IS setting it, so there is no default to configure and no
|
|
101
|
-
// way for the configured answer to drift from the one you actually work in.
|
|
102
|
-
export const ROOT_FILE = 'root.json';
|
|
103
|
-
|
|
104
|
-
export function readRoot(root) {
|
|
105
|
-
try { return JSON.parse(fs.readFileSync(path.join(root, ROOT_FILE), 'utf8')) || {}; }
|
|
106
|
-
catch { return {}; }
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function writeRoot(root, fields) {
|
|
110
|
-
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
111
|
-
const rec = { ...readRoot(root), ...fields };
|
|
112
|
-
writeJsonAtomic(path.join(root, ROOT_FILE), rec);
|
|
113
|
-
return rec;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Which identity you get with no `--profile`: the last one started. Failing
|
|
117
|
-
// that, one identity is unambiguous and needs no ceremony. Two, neither ever
|
|
118
|
-
// started, is a genuine question — and the answer is to ask rather than pick,
|
|
119
|
-
// because picking silently would be picking someone's fediverse account.
|
|
120
|
-
export function defaultProfile(root) {
|
|
121
|
-
const named = readRoot(root).default;
|
|
122
|
-
// Only a home holding a credential counts. A setup that was abandoned leaves a
|
|
123
|
-
// directory behind, and letting that become the default would answer "which
|
|
124
|
-
// identity" with one that does not exist yet.
|
|
125
|
-
const real = identityHomes(root).filter(h => fs.existsSync(path.join(h.dir, 'credential.json')));
|
|
126
|
-
if (named && real.some(h => h.name === named)) return named;
|
|
127
|
-
if (named) return { missing: named };
|
|
128
|
-
return real.length === 1 ? real[0].name : null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export const profileHome = (root, name) => path.join(profilesDir(root), name);
|
|
132
|
-
|
|
133
|
-
// A root from before every identity moved under profiles/. Its presence is what
|
|
134
|
-
// every command checks to tell you to migrate, rather than quietly doing it —
|
|
135
|
-
// this directory holds a private key.
|
|
136
|
-
export const rootHoldsIdentity = (root) => fs.existsSync(path.join(root, 'credential.json'));
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// For DISPLAY only: `~/.fedipod` reads at a glance where
|
|
140
|
-
// `/home/jeff/.fedipod` has to be parsed, and the home directory is
|
|
141
|
-
// the least interesting part of every path this prints. A `file:` URL is a path
|
|
142
|
-
// wearing a scheme, so it is shown as the path — which is also what you would
|
|
143
|
-
// type, since `state --to` takes either.
|
|
144
|
-
//
|
|
145
|
-
// Never used to build a path. `~` is the shell's, not the filesystem's, and
|
|
146
|
-
// anything that resolves one must expand it first.
|
|
147
|
-
export function tildify(p, homedir = os.homedir()) {
|
|
148
|
-
if (!p) return p;
|
|
149
|
-
let s = String(p);
|
|
150
|
-
if (s.startsWith('file://')) { try { s = fileURLToPath(s); } catch { return s; } }
|
|
151
|
-
if (s === homedir) return '~';
|
|
152
|
-
return s.startsWith(homedir + path.sep) ? '~' + s.slice(homedir.length) : s;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Using an identity is what makes it the default. Called when an agent starts
|
|
156
|
-
// for one — not on every read, or `--profile other status` would quietly move
|
|
157
|
-
// the machine's idea of "you" while only asking a question.
|
|
158
|
-
export function recordLastUsed(root, name) {
|
|
159
|
-
if (!root || !name || readRoot(root).default === name) return;
|
|
160
|
-
try { writeRoot(root, { default: name, at: new Date().toISOString() }); } catch { /* not fatal */ }
|
|
161
|
-
}
|