Originally the CLI was using Bun for a lot of the file reading and writing, and building the CLI was using the --compile flag to create a single binary. While it looked like this was working initially, the problem was the architecture was being defaulted to my personal machine rather than being cross platform. Bun has the ability to create cross platform binaries, but then the problem becomes distributing those binaries through install scripts and a whole bunch of other fun stuff. Since we originally launched through npm we will stick with a Node build for now, but will highly consider going back to bun and doing binaries in the future.
+38
-10
bun.lock
+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
+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
+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
+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
+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
+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
-1
packages/cli/src/index.ts
+20
-15
packages/cli/src/lib/atproto.ts
+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
+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
+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
+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
+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
}
History
1 round
0 comments
stevedylan.dev
submitted
#0
1 commit
expand
collapse
chore: refactored codebase to use node and fs instead of bun
expand 0 comments
pull request successfully merged