htmlship 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +53 -53
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -56,7 +56,7 @@ var VERSION;
|
|
|
56
56
|
var init_version = __esm({
|
|
57
57
|
"src/version.ts"() {
|
|
58
58
|
"use strict";
|
|
59
|
-
VERSION = "0.1.
|
|
59
|
+
VERSION = "0.1.3";
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -90,24 +90,24 @@ var init_client = __esm({
|
|
|
90
90
|
if (options.password != null) body["password"] = options.password;
|
|
91
91
|
if (options.expiresIn != null) body["expires_in"] = options.expiresIn;
|
|
92
92
|
if (options.parentSlug != null) body["parent_slug"] = options.parentSlug;
|
|
93
|
-
return await this.request("POST", "/api/v1/
|
|
93
|
+
return await this.request("POST", "/api/v1/pages", { body });
|
|
94
94
|
}
|
|
95
95
|
async get(slug) {
|
|
96
|
-
return await this.request("GET", `/api/v1/
|
|
96
|
+
return await this.request("GET", `/api/v1/pages/${encodeURIComponent(slug)}`);
|
|
97
97
|
}
|
|
98
98
|
async update(slug, html, ownerKey, options = {}) {
|
|
99
99
|
const body = { html };
|
|
100
100
|
if (options.title != null) body["title"] = options.title;
|
|
101
101
|
return await this.request(
|
|
102
102
|
"PATCH",
|
|
103
|
-
`/api/v1/
|
|
103
|
+
`/api/v1/pages/${encodeURIComponent(slug)}`,
|
|
104
104
|
{ body, headers: { "X-Owner-Key": ownerKey } }
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
async delete(slug, ownerKey) {
|
|
108
108
|
await this.request(
|
|
109
109
|
"DELETE",
|
|
110
|
-
`/api/v1/
|
|
110
|
+
`/api/v1/pages/${encodeURIComponent(slug)}`,
|
|
111
111
|
{ headers: { "X-Owner-Key": ownerKey }, expectNoBody: true }
|
|
112
112
|
);
|
|
113
113
|
}
|
|
@@ -121,7 +121,7 @@ var init_client = __esm({
|
|
|
121
121
|
if (options.expiresIn != null) body["expires_in"] = options.expiresIn;
|
|
122
122
|
return await this.request(
|
|
123
123
|
"POST",
|
|
124
|
-
`/api/v1/
|
|
124
|
+
`/api/v1/pages/${encodeURIComponent(parentSlug)}/version`,
|
|
125
125
|
{ body }
|
|
126
126
|
);
|
|
127
127
|
}
|
|
@@ -178,7 +178,7 @@ var init_client = __esm({
|
|
|
178
178
|
}
|
|
179
179
|
return text;
|
|
180
180
|
} catch {
|
|
181
|
-
return
|
|
181
|
+
return `HTTP ${response.status}`;
|
|
182
182
|
}
|
|
183
183
|
} catch {
|
|
184
184
|
return `HTTP ${response.status}`;
|
|
@@ -203,18 +203,18 @@ function buildMcpServer(client) {
|
|
|
203
203
|
server.registerTool(
|
|
204
204
|
"publish_html",
|
|
205
205
|
{
|
|
206
|
-
description: "Publish an HTML document and get a public URL. The owner_key returned is the only credential to update or delete this
|
|
206
|
+
description: "Publish an HTML document and get a public URL. The owner_key returned is the only credential to update or delete this page later \u2014 save it.",
|
|
207
207
|
inputSchema: {
|
|
208
208
|
html: z.string().describe("The HTML body to publish."),
|
|
209
209
|
title: z.string().optional().describe("Optional human-readable title."),
|
|
210
|
-
|
|
210
|
+
expires_in: z.number().int().min(1).max(60 * 24 * 7).optional().describe("Optional TTL in minutes (1\u201310080, i.e. up to 7 days).")
|
|
211
211
|
}
|
|
212
212
|
},
|
|
213
|
-
async ({ html, title,
|
|
213
|
+
async ({ html, title, expires_in }) => {
|
|
214
214
|
try {
|
|
215
|
-
const
|
|
215
|
+
const page = await c.publish(html, {
|
|
216
216
|
title: title ?? null,
|
|
217
|
-
expiresIn:
|
|
217
|
+
expiresIn: expires_in ?? null
|
|
218
218
|
});
|
|
219
219
|
return {
|
|
220
220
|
content: [
|
|
@@ -222,10 +222,10 @@ function buildMcpServer(client) {
|
|
|
222
222
|
type: "text",
|
|
223
223
|
text: JSON.stringify(
|
|
224
224
|
{
|
|
225
|
-
url:
|
|
226
|
-
slug:
|
|
227
|
-
owner_key:
|
|
228
|
-
expires_at:
|
|
225
|
+
url: page.url,
|
|
226
|
+
slug: page.slug,
|
|
227
|
+
owner_key: page.owner_key,
|
|
228
|
+
expires_at: page.expires_at
|
|
229
229
|
},
|
|
230
230
|
null,
|
|
231
231
|
2
|
|
@@ -241,20 +241,20 @@ function buildMcpServer(client) {
|
|
|
241
241
|
server.registerTool(
|
|
242
242
|
"fetch_html",
|
|
243
243
|
{
|
|
244
|
-
description: "Fetch a
|
|
244
|
+
description: "Fetch a page's metadata. To view the rendered HTML, open the `url` in a browser \u2014 the content is served from a sandboxed subdomain.",
|
|
245
245
|
inputSchema: {
|
|
246
|
-
slug: z.string().describe("The
|
|
246
|
+
slug: z.string().describe("The page's short identifier.")
|
|
247
247
|
}
|
|
248
248
|
},
|
|
249
249
|
async ({ slug }) => {
|
|
250
250
|
try {
|
|
251
|
-
const
|
|
251
|
+
const page = await c.get(slug);
|
|
252
252
|
return {
|
|
253
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
253
|
+
content: [{ type: "text", text: JSON.stringify(page, null, 2) }]
|
|
254
254
|
};
|
|
255
255
|
} catch (err) {
|
|
256
256
|
if (err instanceof NotFoundError) {
|
|
257
|
-
return errorResult("fetch_html", new HTMLShipError(`
|
|
257
|
+
return errorResult("fetch_html", new HTMLShipError(`page '${slug}' not found`));
|
|
258
258
|
}
|
|
259
259
|
return errorResult("fetch_html", err);
|
|
260
260
|
}
|
|
@@ -263,9 +263,9 @@ function buildMcpServer(client) {
|
|
|
263
263
|
server.registerTool(
|
|
264
264
|
"update_html",
|
|
265
265
|
{
|
|
266
|
-
description: "Replace the HTML for an existing
|
|
266
|
+
description: "Replace the HTML for an existing page. Requires the original owner_key.",
|
|
267
267
|
inputSchema: {
|
|
268
|
-
slug: z.string().describe("The
|
|
268
|
+
slug: z.string().describe("The page's short identifier."),
|
|
269
269
|
html: z.string().describe("The new HTML body."),
|
|
270
270
|
owner_key: z.string().describe("The owner key returned at publish time."),
|
|
271
271
|
title: z.string().optional().describe("Optional new title.")
|
|
@@ -273,13 +273,13 @@ function buildMcpServer(client) {
|
|
|
273
273
|
},
|
|
274
274
|
async ({ slug, html, owner_key, title }) => {
|
|
275
275
|
try {
|
|
276
|
-
const
|
|
276
|
+
const page = await c.update(slug, html, owner_key, { title: title ?? null });
|
|
277
277
|
return {
|
|
278
278
|
content: [
|
|
279
279
|
{
|
|
280
280
|
type: "text",
|
|
281
281
|
text: JSON.stringify(
|
|
282
|
-
{ url:
|
|
282
|
+
{ url: page.url, slug: page.slug, updated_at: page.updated_at },
|
|
283
283
|
null,
|
|
284
284
|
2
|
|
285
285
|
)
|
|
@@ -386,7 +386,7 @@ function createKeyStore(overrideDir) {
|
|
|
386
386
|
|
|
387
387
|
// src/commands/delete.ts
|
|
388
388
|
function registerDelete(program) {
|
|
389
|
-
program.command("delete").description("Soft-delete a
|
|
389
|
+
program.command("delete").description("Soft-delete a page.").argument("<slug>", "Page slug").option("--owner-key <key>", "Owner key (defaults to local store)").option("-y, --yes", "Skip confirmation").action(async function(slug, opts) {
|
|
390
390
|
const keys = createKeyStore();
|
|
391
391
|
const ownerKey = opts.ownerKey ?? keys.lookupOwnerKey(slug);
|
|
392
392
|
if (!ownerKey) {
|
|
@@ -396,7 +396,7 @@ function registerDelete(program) {
|
|
|
396
396
|
}
|
|
397
397
|
if (!opts.yes) {
|
|
398
398
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
399
|
-
const answer = await rl.question(`Delete
|
|
399
|
+
const answer = await rl.question(`Delete page '${slug}'? [y/N] `);
|
|
400
400
|
rl.close();
|
|
401
401
|
if (!/^y(es)?$/i.test(answer.trim())) {
|
|
402
402
|
process.stderr.write("Aborted.\n");
|
|
@@ -416,16 +416,16 @@ function registerDelete(program) {
|
|
|
416
416
|
init_client();
|
|
417
417
|
init_errors();
|
|
418
418
|
function registerGet(program) {
|
|
419
|
-
program.command("get").description("Show metadata for a
|
|
419
|
+
program.command("get").description("Show metadata for a page.").argument("<slug>", "Page slug").action(async function(slug) {
|
|
420
420
|
const apiUrl = this.parent?.opts()?.apiUrl;
|
|
421
421
|
const client = new HTMLShipClient({ baseUrl: apiUrl });
|
|
422
422
|
try {
|
|
423
|
-
const
|
|
424
|
-
process.stdout.write(`${JSON.stringify(
|
|
423
|
+
const page = await client.get(slug);
|
|
424
|
+
process.stdout.write(`${JSON.stringify(page, null, 2)}
|
|
425
425
|
`);
|
|
426
426
|
} catch (err) {
|
|
427
427
|
if (err instanceof NotFoundError) {
|
|
428
|
-
throw new HTMLShipError(`
|
|
428
|
+
throw new HTMLShipError(`page '${slug}' not found`);
|
|
429
429
|
}
|
|
430
430
|
throw err;
|
|
431
431
|
}
|
|
@@ -436,12 +436,12 @@ function registerGet(program) {
|
|
|
436
436
|
init_client();
|
|
437
437
|
init_errors();
|
|
438
438
|
function registerListMine(program) {
|
|
439
|
-
program.command("list-mine").description("List
|
|
439
|
+
program.command("list-mine").description("List pages whose owner keys are saved locally.").option("--limit <n>", "max entries to show", "20").action(async function(opts) {
|
|
440
440
|
const keys = createKeyStore();
|
|
441
441
|
const data = keys.load();
|
|
442
442
|
const entries = Object.entries(data);
|
|
443
443
|
if (entries.length === 0) {
|
|
444
|
-
process.stdout.write(`No saved
|
|
444
|
+
process.stdout.write(`No saved pages in ${keys.file}.
|
|
445
445
|
`);
|
|
446
446
|
return;
|
|
447
447
|
}
|
|
@@ -452,13 +452,13 @@ function registerListMine(program) {
|
|
|
452
452
|
const rows = await Promise.all(
|
|
453
453
|
sorted.map(async ([slug, info]) => {
|
|
454
454
|
try {
|
|
455
|
-
const
|
|
455
|
+
const page = await client.get(slug);
|
|
456
456
|
return {
|
|
457
457
|
slug,
|
|
458
458
|
url: info.url,
|
|
459
|
-
title:
|
|
460
|
-
size: `${
|
|
461
|
-
views: String(
|
|
459
|
+
title: page.title ?? info.title ?? "",
|
|
460
|
+
size: `${page.size_bytes}B`,
|
|
461
|
+
views: String(page.view_count),
|
|
462
462
|
status: "ok"
|
|
463
463
|
};
|
|
464
464
|
} catch (err) {
|
|
@@ -530,13 +530,13 @@ async function tryClipboardCopy(text) {
|
|
|
530
530
|
|
|
531
531
|
// src/commands/publish.ts
|
|
532
532
|
function registerPublish(program) {
|
|
533
|
-
program.command("publish").description("Publish an HTML document and get a URL.").argument("[source]", "file path, '-' for stdin (default: stdin if piped)").option("-f, --file <path>", "HTML file path").option("--title <title>", "Optional title").option("--password <password>", "Password-protect the
|
|
533
|
+
program.command("publish").description("Publish an HTML document and get a URL.").argument("[source]", "file path, '-' for stdin (default: stdin if piped)").option("-f, --file <path>", "HTML file path").option("--title <title>", "Optional title").option("--password <password>", "Password-protect the page").option("--expires-in <minutes>", "Minutes until expiry (1\u201310080, i.e. up to 7 days)").option("--no-clipboard", "Don't copy URL to clipboard").option("-q, --quiet", "Print only the URL").action(async function(source, opts) {
|
|
534
534
|
const html = readHtmlFromSource(source, opts.file);
|
|
535
535
|
const apiUrl = this.parent?.opts()?.apiUrl;
|
|
536
536
|
const client = new HTMLShipClient({ baseUrl: apiUrl });
|
|
537
|
-
let
|
|
537
|
+
let page;
|
|
538
538
|
try {
|
|
539
|
-
|
|
539
|
+
page = await client.publish(html, {
|
|
540
540
|
title: opts.title ?? null,
|
|
541
541
|
password: opts.password ?? null,
|
|
542
542
|
expiresIn: opts.expiresIn ? Number.parseInt(opts.expiresIn, 10) : null
|
|
@@ -545,28 +545,28 @@ function registerPublish(program) {
|
|
|
545
545
|
throw new HTMLShipError(`publish failed: ${err.message}`);
|
|
546
546
|
}
|
|
547
547
|
const keys = createKeyStore();
|
|
548
|
-
keys.remember(
|
|
549
|
-
owner_key:
|
|
550
|
-
url:
|
|
548
|
+
keys.remember(page.slug, {
|
|
549
|
+
owner_key: page.owner_key,
|
|
550
|
+
url: page.url,
|
|
551
551
|
title: opts.title ?? null
|
|
552
552
|
});
|
|
553
553
|
if (opts.quiet) {
|
|
554
|
-
process.stdout.write(`${
|
|
554
|
+
process.stdout.write(`${page.url}
|
|
555
555
|
`);
|
|
556
556
|
return;
|
|
557
557
|
}
|
|
558
|
-
process.stdout.write(`${
|
|
558
|
+
process.stdout.write(`${page.url}
|
|
559
559
|
`);
|
|
560
|
-
process.stderr.write(`slug: ${
|
|
560
|
+
process.stderr.write(`slug: ${page.slug}
|
|
561
561
|
`);
|
|
562
|
-
process.stderr.write(`owner_key: ${
|
|
562
|
+
process.stderr.write(`owner_key: ${page.owner_key} (saved to ${keys.file})
|
|
563
563
|
`);
|
|
564
|
-
if (
|
|
565
|
-
process.stderr.write(`expires: ${
|
|
564
|
+
if (page.expires_at) {
|
|
565
|
+
process.stderr.write(`expires: ${page.expires_at}
|
|
566
566
|
`);
|
|
567
567
|
}
|
|
568
568
|
if (opts.noClipboard !== true) {
|
|
569
|
-
const copied = await tryClipboardCopy(
|
|
569
|
+
const copied = await tryClipboardCopy(page.url);
|
|
570
570
|
if (copied) process.stderr.write("(URL copied to clipboard)\n");
|
|
571
571
|
}
|
|
572
572
|
});
|
|
@@ -576,7 +576,7 @@ function registerPublish(program) {
|
|
|
576
576
|
init_client();
|
|
577
577
|
init_errors();
|
|
578
578
|
function registerUpdate(program) {
|
|
579
|
-
program.command("update").description("Replace HTML for an existing
|
|
579
|
+
program.command("update").description("Replace HTML for an existing page.").argument("<slug>", "Page slug").argument("[source]", "file path, '-' for stdin").option("-f, --file <path>", "HTML file path").option("--title <title>", "Optional new title").option("--owner-key <key>", "Owner key (defaults to local store)").action(async function(slug, source, opts) {
|
|
580
580
|
const html = readHtmlFromSource(source, opts.file);
|
|
581
581
|
const keys = createKeyStore();
|
|
582
582
|
const ownerKey = opts.ownerKey ?? keys.lookupOwnerKey(slug);
|
|
@@ -587,8 +587,8 @@ function registerUpdate(program) {
|
|
|
587
587
|
}
|
|
588
588
|
const apiUrl = this.parent?.opts()?.apiUrl;
|
|
589
589
|
const client = new HTMLShipClient({ baseUrl: apiUrl });
|
|
590
|
-
const
|
|
591
|
-
process.stdout.write(`${
|
|
590
|
+
const page = await client.update(slug, html, ownerKey, { title: opts.title ?? null });
|
|
591
|
+
process.stdout.write(`${page.url}
|
|
592
592
|
`);
|
|
593
593
|
});
|
|
594
594
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "htmlship",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Host and share HTML pages from LLMs and coding agents in one line. CLI + MCP server.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"html",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"homepage": "https://htmlship.com",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/htmlship/htmlship.git",
|
|
19
|
+
"url": "git+https://github.com/htmlship/htmlship.git",
|
|
20
20
|
"directory": "npm"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"node": ">=18"
|
|
27
27
|
},
|
|
28
28
|
"bin": {
|
|
29
|
-
"htmlship": "
|
|
29
|
+
"htmlship": "dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
32
|
"dist",
|