A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing

Compare changes

Choose any two refs to compare.

+154 -101
+38 -10
bun.lock
··· 24 }, 25 "packages/cli": { 26 "name": "sequoia-cli", 27 - "version": "0.0.6", 28 "bin": { 29 - "sequoia": "dist/sequoia", 30 }, 31 "dependencies": { 32 "@atproto/api": "^0.18.17", 33 "@clack/prompts": "^1.0.0", 34 "cmd-ts": "^0.14.3", 35 }, 36 "devDependencies": { 37 - "@types/bun": "latest", 38 }, 39 "peerDependencies": { 40 "typescript": "^5", ··· 188 189 "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], 190 191 "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 192 193 "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], ··· 524 525 "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], 526 527 "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 528 529 - "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], 530 531 "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], 532 ··· 834 835 "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], 836 837 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 838 839 "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], ··· 1094 1095 "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], 1096 1097 - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 1098 1099 "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], 1100 1101 "mini-svg-data-uri": ["mini-svg-data-uri@1.4.4", "", { "bin": { "mini-svg-data-uri": "cli.js" } }, "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="], 1102 1103 "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], 1104 1105 "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], ··· 1150 1151 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 1152 1153 "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 1154 1155 "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], ··· 1336 1337 "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 1338 1339 - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 1340 1341 "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 1342 ··· 1442 1443 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1444 1445 "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], 1446 1447 "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1448 1449 "create-vocs/@clack/prompts": ["@clack/prompts@0.7.0", "", { "dependencies": { "@clack/core": "^0.3.3", "is-unicode-supported": "*", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA=="], ··· 1456 1457 "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], 1458 1459 "hast-util-from-dom/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], 1460 1461 "hast-util-from-parse5/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], ··· 1474 1475 "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], 1476 1477 "radix-ui/@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], 1478 1479 "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1480 1481 "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1482 1483 - "sequoia-cli/@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], 1484 - 1485 "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1486 1487 "@radix-ui/react-label/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], 1488 1489 "@shikijs/twoslash/twoslash/twoslash-protocol": ["twoslash-protocol@0.2.12", "", {}, "sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg=="], 1490 1491 "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1492 1493 "create-vocs/@clack/prompts/@clack/core": ["@clack/core@0.3.5", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ=="], ··· 1498 1499 "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], 1500 1501 "hast-util-from-dom/hastscript/property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], 1502 1503 "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 1504 1505 "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1506 - 1507 - "sequoia-cli/@types/bun/bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], 1508 } 1509 }
··· 24 }, 25 "packages/cli": { 26 "name": "sequoia-cli", 27 + "version": "0.1.0", 28 "bin": { 29 + "sequoia": "dist/index.js", 30 }, 31 "dependencies": { 32 "@atproto/api": "^0.18.17", 33 "@clack/prompts": "^1.0.0", 34 "cmd-ts": "^0.14.3", 35 + "glob": "^13.0.0", 36 + "mime-types": "^2.1.35", 37 + "minimatch": "^10.1.1", 38 }, 39 "devDependencies": { 40 + "@types/mime-types": "^3.0.1", 41 + "@types/node": "^20", 42 }, 43 "peerDependencies": { 44 "typescript": "^5", ··· 192 193 "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], 194 195 + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], 196 + 197 + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], 198 + 199 "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 200 201 "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], ··· 532 533 "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], 534 535 + "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="], 536 + 537 "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 538 539 + "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], 540 541 "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], 542 ··· 844 845 "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], 846 847 + "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], 848 + 849 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 850 851 "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], ··· 1106 1107 "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], 1108 1109 + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 1110 + 1111 + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 1112 1113 "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], 1114 1115 "mini-svg-data-uri": ["mini-svg-data-uri@1.4.4", "", { "bin": { "mini-svg-data-uri": "cli.js" } }, "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="], 1116 1117 + "minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], 1118 + 1119 + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 1120 + 1121 "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], 1122 1123 "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], ··· 1168 1169 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 1170 1171 + "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], 1172 + 1173 "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 1174 1175 "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], ··· 1356 1357 "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 1358 1359 + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 1360 1361 "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 1362 ··· 1462 1463 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1464 1465 + "bun-types/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], 1466 + 1467 "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], 1468 1469 + "compressible/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 1470 + 1471 "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1472 1473 "create-vocs/@clack/prompts": ["@clack/prompts@0.7.0", "", { "dependencies": { "@clack/core": "^0.3.3", "is-unicode-supported": "*", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA=="], ··· 1480 1481 "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], 1482 1483 + "eval/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], 1484 + 1485 "hast-util-from-dom/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], 1486 1487 "hast-util-from-parse5/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], ··· 1500 1501 "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], 1502 1503 + "path-scurry/lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], 1504 + 1505 "radix-ui/@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], 1506 1507 "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1508 1509 "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1510 1511 "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1512 1513 "@radix-ui/react-label/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], 1514 1515 "@shikijs/twoslash/twoslash/twoslash-protocol": ["twoslash-protocol@0.2.12", "", {}, "sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg=="], 1516 1517 + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 1518 + 1519 "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1520 1521 "create-vocs/@clack/prompts/@clack/core": ["@clack/core@0.3.5", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ=="], ··· 1526 1527 "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], 1528 1529 + "eval/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 1530 + 1531 "hast-util-from-dom/hastscript/property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], 1532 1533 "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 1534 1535 "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1536 } 1537 }
+10 -7
packages/cli/package.json
··· 1 { 2 "name": "sequoia-cli", 3 "version": "0.1.0", 4 - "module": "dist/index.js", 5 "type": "module", 6 "bin": { 7 - "sequoia": "dist/sequoia" 8 }, 9 "files": [ 10 "dist", 11 "README.md" 12 ], 13 - "main": "./dist/sequoia", 14 "exports": { 15 - ".": "./dist/sequoia" 16 }, 17 "scripts": { 18 - "build": "bun build src/index.ts --compile --outfile dist/sequoia", 19 "dev": "bun run build && bun link", 20 "deploy": "bun run build && bun publish" 21 }, 22 "devDependencies": { 23 - "@types/bun": "latest" 24 }, 25 "peerDependencies": { 26 "typescript": "^5" 27 }, 28 "dependencies": { 29 "@atproto/api": "^0.18.17", 30 "cmd-ts": "^0.14.3", 31 - "@clack/prompts": "^1.0.0" 32 } 33 }
··· 1 { 2 "name": "sequoia-cli", 3 "version": "0.1.0", 4 "type": "module", 5 "bin": { 6 + "sequoia": "dist/index.js" 7 }, 8 "files": [ 9 "dist", 10 "README.md" 11 ], 12 + "main": "./dist/index.js", 13 "exports": { 14 + ".": "./dist/index.js" 15 }, 16 "scripts": { 17 + "build": "bun build src/index.ts --target node --outdir dist", 18 "dev": "bun run build && bun link", 19 "deploy": "bun run build && bun publish" 20 }, 21 "devDependencies": { 22 + "@types/mime-types": "^3.0.1", 23 + "@types/node": "^20" 24 }, 25 "peerDependencies": { 26 "typescript": "^5" 27 }, 28 "dependencies": { 29 "@atproto/api": "^0.18.17", 30 + "@clack/prompts": "^1.0.0", 31 "cmd-ts": "^0.14.3", 32 + "glob": "^13.0.0", 33 + "mime-types": "^2.1.35", 34 + "minimatch": "^10.1.1" 35 } 36 }
+18 -8
packages/cli/src/commands/init.ts
··· 1 import { command } from "cmd-ts"; 2 import { 3 intro, ··· 16 import { createAgent, createPublication } from "../lib/atproto"; 17 import type { FrontmatterMapping } from "../lib/types"; 18 19 const onCancel = () => { 20 outro("Setup cancelled"); 21 process.exit(0); ··· 270 }); 271 272 const configPath = path.join(process.cwd(), "sequoia.json"); 273 - await Bun.write(configPath, configContent); 274 275 log.success(`Configuration saved to ${configPath}`); 276 ··· 283 const wellKnownPath = path.join(wellKnownDir, "site.standard.publication"); 284 285 // Ensure .well-known directory exists 286 - await Bun.write(path.join(wellKnownDir, ".gitkeep"), ""); 287 - await Bun.write(wellKnownPath, publicationUri); 288 289 log.success(`Created ${wellKnownPath}`); 290 291 // Update .gitignore 292 const gitignorePath = path.join(process.cwd(), ".gitignore"); 293 - const gitignoreFile = Bun.file(gitignorePath); 294 const stateFilename = ".sequoia-state.json"; 295 296 - if (await gitignoreFile.exists()) { 297 - const gitignoreContent = await gitignoreFile.text(); 298 if (!gitignoreContent.includes(stateFilename)) { 299 - await Bun.write( 300 gitignorePath, 301 gitignoreContent + `\n${stateFilename}\n`, 302 ); 303 log.info(`Added ${stateFilename} to .gitignore`); 304 } 305 } else { 306 - await Bun.write(gitignorePath, `${stateFilename}\n`); 307 log.info(`Created .gitignore with ${stateFilename}`); 308 } 309
··· 1 + import * as fs from "fs/promises"; 2 import { command } from "cmd-ts"; 3 import { 4 intro, ··· 17 import { createAgent, createPublication } from "../lib/atproto"; 18 import type { FrontmatterMapping } from "../lib/types"; 19 20 + async function fileExists(filePath: string): Promise<boolean> { 21 + try { 22 + await fs.access(filePath); 23 + return true; 24 + } catch { 25 + return false; 26 + } 27 + } 28 + 29 const onCancel = () => { 30 outro("Setup cancelled"); 31 process.exit(0); ··· 280 }); 281 282 const configPath = path.join(process.cwd(), "sequoia.json"); 283 + await fs.writeFile(configPath, configContent); 284 285 log.success(`Configuration saved to ${configPath}`); 286 ··· 293 const wellKnownPath = path.join(wellKnownDir, "site.standard.publication"); 294 295 // Ensure .well-known directory exists 296 + await fs.mkdir(wellKnownDir, { recursive: true }); 297 + await fs.writeFile(path.join(wellKnownDir, ".gitkeep"), ""); 298 + await fs.writeFile(wellKnownPath, publicationUri); 299 300 log.success(`Created ${wellKnownPath}`); 301 302 // Update .gitignore 303 const gitignorePath = path.join(process.cwd(), ".gitignore"); 304 const stateFilename = ".sequoia-state.json"; 305 306 + if (await fileExists(gitignorePath)) { 307 + const gitignoreContent = await fs.readFile(gitignorePath, "utf-8"); 308 if (!gitignoreContent.includes(stateFilename)) { 309 + await fs.writeFile( 310 gitignorePath, 311 gitignoreContent + `\n${stateFilename}\n`, 312 ); 313 log.info(`Added ${stateFilename} to .gitignore`); 314 } 315 } else { 316 + await fs.writeFile(gitignorePath, `${stateFilename}\n`); 317 log.info(`Created .gitignore with ${stateFilename}`); 318 } 319
+11 -12
packages/cli/src/commands/inject.ts
··· 1 import { command, flag, option, optional, string } from "cmd-ts"; 2 import { log } from "@clack/prompts"; 3 import * as path from "path"; 4 - import { Glob } from "bun"; 5 import { loadConfig, loadState, findConfig } from "../lib/config"; 6 7 export const injectCommand = command({ ··· 97 log.info(`Found ${pathToAtUri.size} published posts in state`); 98 99 // Scan for HTML files 100 - const glob = new Glob("**/*.html"); 101 - const htmlFiles: string[] = []; 102 - 103 - for await (const file of glob.scan(resolvedOutputDir)) { 104 - htmlFiles.push(path.join(resolvedOutputDir, file)); 105 - } 106 107 if (htmlFiles.length === 0) { 108 log.warn(`No HTML files found in ${resolvedOutputDir}`); ··· 115 let skippedCount = 0; 116 let alreadyHasCount = 0; 117 118 - for (const htmlPath of htmlFiles) { 119 // Try to match this HTML file to a published post 120 - const relativePath = path.relative(resolvedOutputDir, htmlPath); 121 const htmlDir = path.dirname(relativePath); 122 const htmlBasename = path.basename(relativePath, ".html"); 123 ··· 152 } 153 154 // Read the HTML file 155 - const file = Bun.file(htmlPath); 156 - let content = await file.text(); 157 158 // Check if link tag already exists 159 const linkTag = `<link rel="site.standard.document" href="${atUri}">`; ··· 184 `${indent}${linkTag}\n${indent}` + 185 content.slice(headCloseIndex); 186 187 - await Bun.write(htmlPath, content); 188 log.success(` Injected into: ${relativePath}`); 189 injectedCount++; 190 }
··· 1 + import * as fs from "fs/promises"; 2 import { command, flag, option, optional, string } from "cmd-ts"; 3 import { log } from "@clack/prompts"; 4 import * as path from "path"; 5 + import { glob } from "glob"; 6 import { loadConfig, loadState, findConfig } from "../lib/config"; 7 8 export const injectCommand = command({ ··· 98 log.info(`Found ${pathToAtUri.size} published posts in state`); 99 100 // Scan for HTML files 101 + const htmlFiles = await glob("**/*.html", { 102 + cwd: resolvedOutputDir, 103 + absolute: false, 104 + }); 105 106 if (htmlFiles.length === 0) { 107 log.warn(`No HTML files found in ${resolvedOutputDir}`); ··· 114 let skippedCount = 0; 115 let alreadyHasCount = 0; 116 117 + for (const file of htmlFiles) { 118 + const htmlPath = path.join(resolvedOutputDir, file); 119 // Try to match this HTML file to a published post 120 + const relativePath = file; 121 const htmlDir = path.dirname(relativePath); 122 const htmlBasename = path.basename(relativePath, ".html"); 123 ··· 152 } 153 154 // Read the HTML file 155 + let content = await fs.readFile(htmlPath, "utf-8"); 156 157 // Check if link tag already exists 158 const linkTag = `<link rel="site.standard.document" href="${atUri}">`; ··· 183 `${indent}${linkTag}\n${indent}` + 184 content.slice(headCloseIndex); 185 186 + await fs.writeFile(htmlPath, content); 187 log.success(` Injected into: ${relativePath}`); 188 injectedCount++; 189 }
+3 -2
packages/cli/src/commands/publish.ts
··· 1 import { command, flag } from "cmd-ts"; 2 import { select, spinner, log } from "@clack/prompts"; 3 import * as path from "path"; ··· 164 // Handle cover image upload 165 let coverImage: BlobObject | undefined; 166 if (post.frontmatter.ogImage) { 167 - const imagePath = resolveImagePath( 168 post.frontmatter.ogImage, 169 imagesDir, 170 contentDir ··· 191 192 // Update frontmatter with atUri 193 const updatedContent = updateFrontmatterWithAtUri(post.rawContent, atUri); 194 - await Bun.write(post.filePath, updatedContent); 195 log.info(` Updated frontmatter in ${path.basename(post.filePath)}`); 196 197 // Use updated content (with atUri) for hash so next run sees matching hash
··· 1 + import * as fs from "fs/promises"; 2 import { command, flag } from "cmd-ts"; 3 import { select, spinner, log } from "@clack/prompts"; 4 import * as path from "path"; ··· 165 // Handle cover image upload 166 let coverImage: BlobObject | undefined; 167 if (post.frontmatter.ogImage) { 168 + const imagePath = await resolveImagePath( 169 post.frontmatter.ogImage, 170 imagesDir, 171 contentDir ··· 192 193 // Update frontmatter with atUri 194 const updatedContent = updateFrontmatterWithAtUri(post.rawContent, atUri); 195 + await fs.writeFile(post.filePath, updatedContent); 196 log.info(` Updated frontmatter in ${path.basename(post.filePath)}`); 197 198 // Use updated content (with atUri) for hash so next run sees matching hash
+3 -3
packages/cli/src/commands/sync.ts
··· 1 import { command, flag } from "cmd-ts"; 2 import { select, spinner, log } from "@clack/prompts"; 3 import * as path from "path"; ··· 169 if (frontmatterUpdates.length > 0) { 170 s.start(`Updating frontmatter in ${frontmatterUpdates.length} files...`); 171 for (const { filePath, atUri } of frontmatterUpdates) { 172 - const file = Bun.file(filePath); 173 - const content = await file.text(); 174 const updated = updateFrontmatterWithAtUri(content, atUri); 175 - await Bun.write(filePath, updated); 176 log.message(` Updated: ${path.basename(filePath)}`); 177 } 178 s.stop("Frontmatter updated");
··· 1 + import * as fs from "fs/promises"; 2 import { command, flag } from "cmd-ts"; 3 import { select, spinner, log } from "@clack/prompts"; 4 import * as path from "path"; ··· 170 if (frontmatterUpdates.length > 0) { 171 s.start(`Updating frontmatter in ${frontmatterUpdates.length} files...`); 172 for (const { filePath, atUri } of frontmatterUpdates) { 173 + const content = await fs.readFile(filePath, "utf-8"); 174 const updated = updateFrontmatterWithAtUri(content, atUri); 175 + await fs.writeFile(filePath, updated); 176 log.message(` Updated: ${path.basename(filePath)}`); 177 } 178 s.stop("Frontmatter updated");
+1 -1
packages/cli/src/index.ts
··· 1 - #!/usr/bin/env bun 2 3 import { run, subcommands } from "cmd-ts"; 4 import { authCommand } from "./commands/auth";
··· 1 + #!/usr/bin/env node 2 3 import { run, subcommands } from "cmd-ts"; 4 import { authCommand } from "./commands/auth";
+20 -15
packages/cli/src/lib/atproto.ts
··· 1 import { AtpAgent } from "@atproto/api"; 2 import * as path from "path"; 3 import type { Credentials, BlogPost, BlobObject, PublisherConfig } from "./types"; 4 import { stripMarkdownForText } from "./markdown"; 5 6 export async function resolveHandleToPDS(handle: string): Promise<string> { 7 // First, resolve the handle to a DID 8 let did: string; ··· 87 agent: AtpAgent, 88 imagePath: string 89 ): Promise<BlobObject | undefined> { 90 - const file = Bun.file(imagePath); 91 - 92 - if (!(await file.exists())) { 93 return undefined; 94 } 95 96 try { 97 - const imageBuffer = await file.arrayBuffer(); 98 - const mimeType = file.type || "application/octet-stream"; 99 100 const response = await agent.com.atproto.repo.uploadBlob( 101 new Uint8Array(imageBuffer), ··· 118 } 119 } 120 121 - export function resolveImagePath( 122 ogImage: string, 123 imagesDir: string | undefined, 124 contentDir: string 125 - ): string | null { 126 // Try multiple resolution strategies 127 const filename = path.basename(ogImage); 128 129 // 1. If imagesDir is specified, look there 130 if (imagesDir) { 131 const imagePath = path.join(imagesDir, filename); 132 - try { 133 - const stat = Bun.file(imagePath); 134 if (stat.size > 0) { 135 return imagePath; 136 } 137 - } catch { 138 - // File doesn't exist, continue 139 } 140 } 141 ··· 146 147 // 3. Try relative to content directory 148 const contentRelative = path.join(contentDir, ogImage); 149 - try { 150 - const stat = Bun.file(contentRelative); 151 if (stat.size > 0) { 152 return contentRelative; 153 } 154 - } catch { 155 - // File doesn't exist 156 } 157 158 return null;
··· 1 import { AtpAgent } from "@atproto/api"; 2 + import * as fs from "fs/promises"; 3 import * as path from "path"; 4 + import * as mimeTypes from "mime-types"; 5 import type { Credentials, BlogPost, BlobObject, PublisherConfig } from "./types"; 6 import { stripMarkdownForText } from "./markdown"; 7 8 + async function fileExists(filePath: string): Promise<boolean> { 9 + try { 10 + await fs.access(filePath); 11 + return true; 12 + } catch { 13 + return false; 14 + } 15 + } 16 + 17 export async function resolveHandleToPDS(handle: string): Promise<string> { 18 // First, resolve the handle to a DID 19 let did: string; ··· 98 agent: AtpAgent, 99 imagePath: string 100 ): Promise<BlobObject | undefined> { 101 + if (!(await fileExists(imagePath))) { 102 return undefined; 103 } 104 105 try { 106 + const imageBuffer = await fs.readFile(imagePath); 107 + const mimeType = mimeTypes.lookup(imagePath) || "application/octet-stream"; 108 109 const response = await agent.com.atproto.repo.uploadBlob( 110 new Uint8Array(imageBuffer), ··· 127 } 128 } 129 130 + export async function resolveImagePath( 131 ogImage: string, 132 imagesDir: string | undefined, 133 contentDir: string 134 + ): Promise<string | null> { 135 // Try multiple resolution strategies 136 const filename = path.basename(ogImage); 137 138 // 1. If imagesDir is specified, look there 139 if (imagesDir) { 140 const imagePath = path.join(imagesDir, filename); 141 + if (await fileExists(imagePath)) { 142 + const stat = await fs.stat(imagePath); 143 if (stat.size > 0) { 144 return imagePath; 145 } 146 } 147 } 148 ··· 153 154 // 3. Try relative to content directory 155 const contentRelative = path.join(contentDir, ogImage); 156 + if (await fileExists(contentRelative)) { 157 + const stat = await fs.stat(contentRelative); 158 if (stat.size > 0) { 159 return contentRelative; 160 } 161 } 162 163 return null;
+15 -8
packages/cli/src/lib/config.ts
··· 1 import * as path from "path"; 2 import type { PublisherConfig, PublisherState, FrontmatterMapping } from "./types"; 3 4 const CONFIG_FILENAME = "sequoia.json"; 5 const STATE_FILENAME = ".sequoia-state.json"; 6 7 export async function findConfig( 8 startDir: string = process.cwd(), 9 ): Promise<string | null> { ··· 11 12 while (true) { 13 const configPath = path.join(currentDir, CONFIG_FILENAME); 14 - const file = Bun.file(configPath); 15 16 - if (await file.exists()) { 17 return configPath; 18 } 19 ··· 38 } 39 40 try { 41 - const file = Bun.file(resolvedPath); 42 - const content = await file.text(); 43 const config = JSON.parse(content) as PublisherConfig; 44 45 // Validate required fields ··· 109 110 export async function loadState(configDir: string): Promise<PublisherState> { 111 const statePath = path.join(configDir, STATE_FILENAME); 112 - const file = Bun.file(statePath); 113 114 - if (!(await file.exists())) { 115 return { posts: {} }; 116 } 117 118 try { 119 - const content = await file.text(); 120 return JSON.parse(content) as PublisherState; 121 } catch { 122 return { posts: {} }; ··· 128 state: PublisherState, 129 ): Promise<void> { 130 const statePath = path.join(configDir, STATE_FILENAME); 131 - await Bun.write(statePath, JSON.stringify(state, null, 2)); 132 } 133 134 export function getStatePath(configDir: string): string {
··· 1 + import * as fs from "fs/promises"; 2 import * as path from "path"; 3 import type { PublisherConfig, PublisherState, FrontmatterMapping } from "./types"; 4 5 const CONFIG_FILENAME = "sequoia.json"; 6 const STATE_FILENAME = ".sequoia-state.json"; 7 8 + async function fileExists(filePath: string): Promise<boolean> { 9 + try { 10 + await fs.access(filePath); 11 + return true; 12 + } catch { 13 + return false; 14 + } 15 + } 16 + 17 export async function findConfig( 18 startDir: string = process.cwd(), 19 ): Promise<string | null> { ··· 21 22 while (true) { 23 const configPath = path.join(currentDir, CONFIG_FILENAME); 24 25 + if (await fileExists(configPath)) { 26 return configPath; 27 } 28 ··· 47 } 48 49 try { 50 + const content = await fs.readFile(resolvedPath, "utf-8"); 51 const config = JSON.parse(content) as PublisherConfig; 52 53 // Validate required fields ··· 117 118 export async function loadState(configDir: string): Promise<PublisherState> { 119 const statePath = path.join(configDir, STATE_FILENAME); 120 121 + if (!(await fileExists(statePath))) { 122 return { posts: {} }; 123 } 124 125 try { 126 + const content = await fs.readFile(statePath, "utf-8"); 127 return JSON.parse(content) as PublisherState; 128 } catch { 129 return { posts: {} }; ··· 135 state: PublisherState, 136 ): Promise<void> { 137 const statePath = path.join(configDir, STATE_FILENAME); 138 + await fs.writeFile(statePath, JSON.stringify(state, null, 2)); 139 } 140 141 export function getStatePath(configDir: string): string {
+15 -6
packages/cli/src/lib/credentials.ts
··· 1 import * as path from "path"; 2 import * as os from "os"; 3 import type { Credentials } from "./types"; ··· 8 // Stored credentials keyed by identifier 9 type CredentialsStore = Record<string, Credentials>; 10 11 /** 12 * Load all stored credentials 13 */ 14 async function loadCredentialsStore(): Promise<CredentialsStore> { 15 - const file = Bun.file(CREDENTIALS_FILE); 16 - if (!(await file.exists())) { 17 return {}; 18 } 19 20 try { 21 - const content = await file.text(); 22 const parsed = JSON.parse(content); 23 24 // Handle legacy single-credential format (migrate on read) ··· 37 * Save the entire credentials store 38 */ 39 async function saveCredentialsStore(store: CredentialsStore): Promise<void> { 40 - await Bun.$`mkdir -p ${CONFIG_DIR}`; 41 - await Bun.write(CREDENTIALS_FILE, JSON.stringify(store, null, 2)); 42 - await Bun.$`chmod 600 ${CREDENTIALS_FILE}`; 43 } 44 45 /**
··· 1 + import * as fs from "fs/promises"; 2 import * as path from "path"; 3 import * as os from "os"; 4 import type { Credentials } from "./types"; ··· 9 // Stored credentials keyed by identifier 10 type CredentialsStore = Record<string, Credentials>; 11 12 + async function fileExists(filePath: string): Promise<boolean> { 13 + try { 14 + await fs.access(filePath); 15 + return true; 16 + } catch { 17 + return false; 18 + } 19 + } 20 + 21 /** 22 * Load all stored credentials 23 */ 24 async function loadCredentialsStore(): Promise<CredentialsStore> { 25 + if (!(await fileExists(CREDENTIALS_FILE))) { 26 return {}; 27 } 28 29 try { 30 + const content = await fs.readFile(CREDENTIALS_FILE, "utf-8"); 31 const parsed = JSON.parse(content); 32 33 // Handle legacy single-credential format (migrate on read) ··· 46 * Save the entire credentials store 47 */ 48 async function saveCredentialsStore(store: CredentialsStore): Promise<void> { 49 + await fs.mkdir(CONFIG_DIR, { recursive: true }); 50 + await fs.writeFile(CREDENTIALS_FILE, JSON.stringify(store, null, 2)); 51 + await fs.chmod(CREDENTIALS_FILE, 0o600); 52 } 53 54 /**
+9 -9
packages/cli/src/lib/markdown.ts
··· 1 import * as path from "path"; 2 - import { Glob } from "bun"; 3 import type { PostFrontmatter, BlogPost, FrontmatterMapping } from "./types"; 4 5 export function parseFrontmatter(content: string, mapping?: FrontmatterMapping): { ··· 120 121 function shouldIgnore(relativePath: string, ignorePatterns: string[]): boolean { 122 for (const pattern of ignorePatterns) { 123 - const glob = new Glob(pattern); 124 - if (glob.match(relativePath)) { 125 return true; 126 } 127 } ··· 137 const posts: BlogPost[] = []; 138 139 for (const pattern of patterns) { 140 - const glob = new Glob(pattern); 141 - 142 - for await (const relativePath of glob.scan({ 143 cwd: contentDir, 144 absolute: false, 145 - })) { 146 // Skip files matching ignore patterns 147 if (shouldIgnore(relativePath, ignorePatterns)) { 148 continue; 149 } 150 151 const filePath = path.join(contentDir, relativePath); 152 - const file = Bun.file(filePath); 153 - const rawContent = await file.text(); 154 155 try { 156 const { frontmatter, body } = parseFrontmatter(rawContent, frontmatterMapping);
··· 1 + import * as fs from "fs/promises"; 2 import * as path from "path"; 3 + import { glob } from "glob"; 4 + import { minimatch } from "minimatch"; 5 import type { PostFrontmatter, BlogPost, FrontmatterMapping } from "./types"; 6 7 export function parseFrontmatter(content: string, mapping?: FrontmatterMapping): { ··· 122 123 function shouldIgnore(relativePath: string, ignorePatterns: string[]): boolean { 124 for (const pattern of ignorePatterns) { 125 + if (minimatch(relativePath, pattern)) { 126 return true; 127 } 128 } ··· 138 const posts: BlogPost[] = []; 139 140 for (const pattern of patterns) { 141 + const files = await glob(pattern, { 142 cwd: contentDir, 143 absolute: false, 144 + }); 145 + 146 + for (const relativePath of files) { 147 // Skip files matching ignore patterns 148 if (shouldIgnore(relativePath, ignorePatterns)) { 149 continue; 150 } 151 152 const filePath = path.join(contentDir, relativePath); 153 + const rawContent = await fs.readFile(filePath, "utf-8"); 154 155 try { 156 const { frontmatter, body } = parseFrontmatter(rawContent, frontmatterMapping);
+11 -20
packages/cli/tsconfig.json
··· 1 { 2 "compilerOptions": { 3 - // Environment setup & latest features 4 - "lib": ["ESNext"], 5 - "target": "ESNext", 6 - "module": "Preserve", 7 - "moduleDetection": "force", 8 - "jsx": "react-jsx", 9 - "allowJs": true, 10 - 11 - // Bundler mode 12 "moduleResolution": "bundler", 13 - "allowImportingTsExtensions": true, 14 - "verbatimModuleSyntax": true, 15 - "noEmit": true, 16 - 17 - // Best practices 18 "strict": true, 19 "skipLibCheck": true, 20 "noFallthroughCasesInSwitch": true, 21 "noUncheckedIndexedAccess": true, 22 - "noImplicitOverride": true, 23 - 24 - // Some stricter flags (disabled by default) 25 "noUnusedLocals": false, 26 - "noUnusedParameters": false, 27 - "noPropertyAccessFromIndexSignature": false, 28 - "composite": true 29 }, 30 "include": ["src"] 31 }
··· 1 { 2 "compilerOptions": { 3 + "lib": ["ES2022"], 4 + "target": "ES2022", 5 + "module": "ESNext", 6 "moduleResolution": "bundler", 7 + "outDir": "./dist", 8 + "rootDir": "./src", 9 + "declaration": true, 10 + "sourceMap": true, 11 "strict": true, 12 "skipLibCheck": true, 13 + "esModuleInterop": true, 14 + "resolveJsonModule": true, 15 + "forceConsistentCasingInFileNames": true, 16 "noFallthroughCasesInSwitch": true, 17 "noUncheckedIndexedAccess": true, 18 "noUnusedLocals": false, 19 + "noUnusedParameters": false 20 }, 21 "include": ["src"] 22 }