@androff/posthtml-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +25 -0
  2. package/dist/index.js +115 -0
  3. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # posthtml
2
+
3
+ CLI tool to upload HTML plans and get a shareable URL.
4
+
5
+ ```
6
+ npm install -g posthtml
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ```
12
+ ptd setup --key <key> # save an API key from https://posthtml.vercel.app/dashboard
13
+ ptd upload index.html # upload a plan → https://posthtml.vercel.app/p/<id>
14
+ ptd list # list your plans
15
+ ptd delete <id> # delete a plan
16
+ ptd replace <id> file.html # replace content, same URL
17
+ ```
18
+
19
+ ## Environment variables
20
+
21
+ | Variable | Default | Description |
22
+ |---|---|---|
23
+ | `PTD_URL` | `http://localhost:3000` | Server base URL |
24
+ | `PTD_API_KEY` | — | API key (overrides `ptd setup`) |
25
+ | `PLANTODO_API_KEY` | — | Legacy alias for `PTD_API_KEY` |
package/dist/index.js ADDED
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { readFileSync as readFileSync2 } from "fs";
5
+ import { resolve as resolve2 } from "path";
6
+ import { Command } from "commander";
7
+
8
+ // src/config.ts
9
+ import { homedir } from "os";
10
+ import { writeFileSync, readFileSync, mkdirSync, existsSync } from "fs";
11
+ import { resolve } from "path";
12
+ var CONFIG_DIR = resolve(homedir(), ".ptd");
13
+ var CONFIG_FILE = resolve(CONFIG_DIR, "config.json");
14
+ function loadConfig() {
15
+ if (!existsSync(CONFIG_FILE)) return null;
16
+ try {
17
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ function saveConfig(config2) {
23
+ if (!existsSync(CONFIG_DIR)) {
24
+ mkdirSync(CONFIG_DIR, { recursive: true });
25
+ }
26
+ writeFileSync(CONFIG_FILE, JSON.stringify(config2, null, 2));
27
+ }
28
+
29
+ // src/index.ts
30
+ var config = loadConfig();
31
+ var API_KEY = process.env.PTD_API_KEY ?? process.env.PLANTODO_API_KEY ?? config?.api_key ?? "";
32
+ var BASE_URL = (process.env.PTD_URL ?? "http://localhost:3000").replace(/\/+$/, "");
33
+ async function api(path, init) {
34
+ const res = await fetch(`${BASE_URL}${path}`, {
35
+ ...init,
36
+ headers: {
37
+ ...init?.headers,
38
+ "x-api-key": API_KEY,
39
+ "Content-Type": "application/json"
40
+ }
41
+ });
42
+ const body = await res.json();
43
+ if (!res.ok) {
44
+ console.error(
45
+ `Error ${res.status}:`,
46
+ body.error ?? body.message ?? JSON.stringify(body)
47
+ );
48
+ process.exit(1);
49
+ }
50
+ return body;
51
+ }
52
+ var program = new Command().name("ptd").description("PostHTML CLI \u2014 upload, list, delete, and replace plans").version("0.1.0");
53
+ program.command("upload <file>").description("Upload an HTML plan file").action(async (file) => {
54
+ const html = readFileSync2(resolve2(file), "utf-8");
55
+ const result = await api("/api/plans", {
56
+ method: "POST",
57
+ body: JSON.stringify({ html, title: file })
58
+ });
59
+ console.log(result.url);
60
+ });
61
+ program.command("list").alias("ls").description("List your plans").action(async () => {
62
+ const plans = await api("/api/plans");
63
+ if (plans.length === 0) {
64
+ console.log("No plans found.");
65
+ return;
66
+ }
67
+ for (const p of plans) {
68
+ const title = p.title || "(untitled)";
69
+ const created = new Date(p.createdAt).toLocaleDateString();
70
+ console.log(`${p.id} ${title} ${created}`);
71
+ }
72
+ });
73
+ program.command("delete <id>").description("Delete a plan").action(async (id) => {
74
+ await api(`/api/plans/${id}`, { method: "DELETE" });
75
+ console.log(`Deleted ${id}`);
76
+ });
77
+ program.command("replace <id> <file>").description("Replace a plan with a new HTML file (preserves ID)").action(async (id, file) => {
78
+ const html = readFileSync2(resolve2(file), "utf-8");
79
+ const result = await api(`/api/plans/${id}`, {
80
+ method: "PATCH",
81
+ body: JSON.stringify({ html })
82
+ });
83
+ console.log(result.url);
84
+ });
85
+ program.command("setup").description("Save API key to ~/.ptd/config.json").option("-k, --key <key>", "API key (prompts if omitted)").action(async () => {
86
+ let key = program.opts().key;
87
+ if (!key) {
88
+ console.log(`Get your API key from: ${BASE_URL}/dashboard`);
89
+ const buf = await new Promise((resolve3) => {
90
+ process.stdout.write("Enter your API key: ");
91
+ process.stdin.setEncoding("utf-8");
92
+ process.stdin.once("data", (d) => resolve3(d.trim()));
93
+ });
94
+ key = buf;
95
+ }
96
+ if (!key) {
97
+ console.error("No API key provided");
98
+ process.exit(1);
99
+ }
100
+ saveConfig({ api_key: key });
101
+ console.log("\u2713 saved to ~/.ptd/config.json");
102
+ });
103
+ async function main() {
104
+ if (!API_KEY) {
105
+ console.error(
106
+ "Set PTD_API_KEY or run 'ptd setup' to configure your API key."
107
+ );
108
+ process.exit(1);
109
+ }
110
+ await program.parseAsync(process.argv);
111
+ }
112
+ main().catch((err) => {
113
+ console.error(err instanceof Error ? err.message : String(err));
114
+ process.exit(1);
115
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@androff/posthtml-cli",
3
+ "version": "0.1.0",
4
+ "description": "PostHTML — CLI tool to upload/delete/list plans",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "plantodo",
8
+ "ptd",
9
+ "plans",
10
+ "html"
11
+ ],
12
+ "homepage": "https://posthtml.vercel.app",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/androdotdev/plantodo.git"
16
+ },
17
+ "type": "module",
18
+ "bin": {
19
+ "ptd": "./dist/index.js"
20
+ },
21
+ "main": "./dist/index.js",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js",
28
+ "default": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "prepublishOnly": "bun run build",
37
+ "clean": "rm -rf dist",
38
+ "test": "echo \"No tests yet\" && exit 0",
39
+ "lint": "echo \"No linter configured\" && exit 0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "26.0.1",
43
+ "tsup": "^8.5.1",
44
+ "typescript": "6.0.3"
45
+ },
46
+ "dependencies": {
47
+ "commander": "^15.0.0"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ }
52
+ }