@sprid/cli 0.1.2

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +14 -0
  3. package/README-post.md +615 -0
  4. package/README.md +249 -0
  5. package/SECURITY.md +18 -0
  6. package/bin.mjs +12 -0
  7. package/package.json +47 -0
  8. package/src/args.mjs +134 -0
  9. package/src/browser.mjs +28 -0
  10. package/src/cli.mjs +257 -0
  11. package/src/commands/auth.mjs +192 -0
  12. package/src/commands/completion.mjs +90 -0
  13. package/src/commands/connect.mjs +586 -0
  14. package/src/commands/docs.mjs +53 -0
  15. package/src/commands/family.mjs +96 -0
  16. package/src/commands/init.mjs +137 -0
  17. package/src/commands/local-tools.mjs +31 -0
  18. package/src/commands/marketing-review.mjs +106 -0
  19. package/src/commands/mcp.mjs +53 -0
  20. package/src/commands/misc.mjs +94 -0
  21. package/src/commands/pinterest.mjs +120 -0
  22. package/src/commands/plan.mjs +230 -0
  23. package/src/commands/post.mjs +41 -0
  24. package/src/commands/reviews.mjs +149 -0
  25. package/src/commands/setup.mjs +220 -0
  26. package/src/commands/status.mjs +217 -0
  27. package/src/commands/studio.mjs +69 -0
  28. package/src/commands/update.mjs +69 -0
  29. package/src/creds.mjs +126 -0
  30. package/src/docs/commands.mjs +152 -0
  31. package/src/docs/guides.generated.mjs +1178 -0
  32. package/src/docs/help.mjs +77 -0
  33. package/src/docs/index.d.mts +18 -0
  34. package/src/docs/index.mjs +45 -0
  35. package/src/docs/queries.d.mts +11 -0
  36. package/src/docs/queries.mjs +77 -0
  37. package/src/endpoint.mjs +17 -0
  38. package/src/evidence.mjs +15 -0
  39. package/src/format.mjs +71 -0
  40. package/src/http.mjs +117 -0
  41. package/src/pending.mjs +27 -0
  42. package/src/post/cli.mjs +2285 -0
  43. package/src/post/json-worker.mjs +12 -0
  44. package/src/post/preview-server.mjs +58 -0
  45. package/src/post/preview.mjs +660 -0
  46. package/src/post/recipes/screen.mjs +290 -0
  47. package/src/post/recipes/stills.mjs +146 -0
  48. package/src/post/rules/platform-rules.d.mts +27 -0
  49. package/src/post/rules/platform-rules.mjs +164 -0
  50. package/src/post/screen/captions.mjs +131 -0
  51. package/src/post/screen/compose.mjs +284 -0
  52. package/src/post/screen/input.mjs +163 -0
  53. package/src/post/screen/sim.mjs +443 -0
  54. package/src/post/screen/simkit.swift +328 -0
  55. package/src/profiles.mjs +61 -0
  56. package/src/release.ts +2 -0
  57. package/src/screenshots.ts +1 -0
  58. package/src/updates.mjs +152 -0
@@ -0,0 +1,12 @@
1
+ // Executed only by the post command's JSON adapter. Never writes JSON to stdout.
2
+ import { run } from "./cli.mjs";
3
+ try {
4
+ const result = await run(process.argv.slice(2));
5
+ const exitCode = result?.exitCode ?? 0;
6
+ process.send({ ok: exitCode === 0, ...result, exitCode });
7
+ process.exitCode = exitCode;
8
+ } catch (error) {
9
+ process.send({ ok: false, error: error.message, exitCode: 1 });
10
+ process.exitCode = 1;
11
+ }
12
+ process.disconnect();
@@ -0,0 +1,58 @@
1
+ import { createReadStream, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const loopback = new Set(['localhost', '127.0.0.1', '[::1]']);
5
+
6
+ export function previewHandler({ directory, account, clients }) {
7
+ return (req, res) => {
8
+ try {
9
+ const base = new URL(`http://${req.headers.host}`);
10
+ if (!loopback.has(base.hostname) || req.headers['sec-fetch-site'] === 'cross-site') return res.writeHead(403).end('forbidden');
11
+ if (req.headers.origin && new URL(req.headers.origin).origin !== base.origin) return res.writeHead(403).end('forbidden');
12
+ if (!['GET', 'HEAD'].includes(req.method)) return res.writeHead(405, { Allow: 'GET, HEAD' }).end();
13
+ const url = new URL(req.url, base);
14
+ if (url.origin !== base.origin) return res.writeHead(400).end('invalid URL');
15
+ const path = decodeURIComponent(url.pathname);
16
+ if (path === '/__whoami') {
17
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
18
+ return res.end(req.method === 'HEAD' ? undefined : JSON.stringify({ account }));
19
+ }
20
+ if (path === '/__reload') {
21
+ res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-store', Connection: 'keep-alive' });
22
+ if (req.method === 'HEAD') return res.end();
23
+ res.write('retry: 1000\n\n'); clients.add(res);
24
+ req.on('close', () => clients.delete(res));
25
+ return;
26
+ }
27
+ // Only generated HTML and individually named media are public. Media links
28
+ // intentionally target the user's selected source files outside this dir.
29
+ const isIndex = path === '/' || path === '/index.html';
30
+ if (!isIndex && !/^\/media\/[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*\.(?:mp4|mov|webm|png|jpe?g|webp)$/i.test(path)) return res.writeHead(404).end('not found');
31
+ const file = join(directory, isIndex ? 'index.html' : path.slice(1));
32
+ const stat = statSync(file);
33
+ if (!stat.isFile()) return res.writeHead(404).end('not found');
34
+ const extension = file.split('.').pop().toLowerCase();
35
+ const types = { html: 'text/html; charset=utf-8', mp4: 'video/mp4', mov: 'video/quicktime', webm: 'video/webm', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp' };
36
+ const headers = { 'Content-Type': types[extension], 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'no-store', 'Accept-Ranges': 'bytes' };
37
+ let start = 0, end = stat.size - 1, status = 200;
38
+ if (req.headers.range) {
39
+ const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range);
40
+ if (!range || (!range[1] && !range[2]) || !stat.size) return res.writeHead(416, { 'Content-Range': `bytes */${stat.size}` }).end();
41
+ start = range[1] ? Number(range[1]) : Math.max(0, stat.size - Number(range[2]));
42
+ end = range[1] && range[2] ? Math.min(Number(range[2]), stat.size - 1) : stat.size - 1;
43
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start > end || start >= stat.size) return res.writeHead(416, { 'Content-Range': `bytes */${stat.size}` }).end();
44
+ status = 206; headers['Content-Range'] = `bytes ${start}-${end}/${stat.size}`;
45
+ }
46
+ headers['Content-Length'] = stat.size ? end - start + 1 : 0;
47
+ res.writeHead(status, headers);
48
+ if (req.method === 'HEAD' || !stat.size) return res.end();
49
+ const stream = createReadStream(file, { start, end });
50
+ stream.on('error', () => res.destroy());
51
+ res.on('close', () => stream.destroy());
52
+ stream.pipe(res);
53
+ } catch (error) {
54
+ if (!res.headersSent) res.writeHead(error.code === 'ENOENT' ? 404 : 400).end('invalid request');
55
+ else res.destroy();
56
+ }
57
+ };
58
+ }