shiply-cli 0.2.0 → 0.3.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.
- package/README.md +5 -1
- package/dist/index.js +55 -35
- package/dist/publish.js +1 -0
- package/dist/state.js +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,12 +13,16 @@ curl -fsSL https://shiply.now/install.sh | bash
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
shiply publish ./dist # publish a directory, print the live URL
|
|
16
|
+
shiply publish ./dist # run it AGAIN → updates the SAME site (no new subdomain)
|
|
16
17
|
shiply publish ./dist --spa # single-page app mode
|
|
17
18
|
shiply login # email a 6-digit code, mint + save an API key
|
|
18
|
-
shiply update ./dist --claim-token <token> # push a new version to an anonymous site
|
|
19
19
|
shiply status <slug-or-domain> [--wait] # SSL + readiness check — confetti when live 🎉
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
shiply remembers each directory's site in `.shiply.json` (slug + update token),
|
|
23
|
+
so repeat publishes always hit the same URL. Use `--new-site` to start fresh;
|
|
24
|
+
gitignore `.shiply.json` in public repos.
|
|
25
|
+
|
|
22
26
|
`shiply status` prints stable `SSL_READY` / `SITE_READY` markers for agents and
|
|
23
27
|
exits 0 only when the certificate is valid and the site serves. `--wait` polls
|
|
24
28
|
until ready (great while a custom domain's certificate issues).
|
package/dist/index.js
CHANGED
|
@@ -4,32 +4,35 @@ import { parseArgs } from 'node:util';
|
|
|
4
4
|
import { confetti } from './confetti.js';
|
|
5
5
|
import { loadApiKey, saveApiKey } from './config.js';
|
|
6
6
|
import { api, DEFAULT_BASE, publish, resolveBase } from './publish.js';
|
|
7
|
+
import { readState, writeState } from './state.js';
|
|
7
8
|
import { checkReadiness, targetToHostname } from './status.js';
|
|
8
|
-
const HELP = `shiply — instant static hosting for agents (https://shiply.now)
|
|
9
|
-
|
|
10
|
-
Usage:
|
|
11
|
-
shiply publish <dir> [options] Publish a directory, print the live URL
|
|
12
|
-
|
|
13
|
-
shiply
|
|
14
|
-
shiply
|
|
15
|
-
shiply
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
const HELP = `shiply — instant static hosting for agents (https://shiply.now)
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
shiply publish <dir> [options] Publish a directory, print the live URL.
|
|
13
|
+
Re-running UPDATES the same site (state in .shiply.json)
|
|
14
|
+
shiply update <dir> Same as publish when .shiply.json exists
|
|
15
|
+
shiply status <slug-or-domain> [--wait] SSL + readiness check (agent-friendly output)
|
|
16
|
+
shiply login [--email <address>] Email a 6-digit code, mint + save an API key
|
|
17
|
+
shiply help
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--spa Single-page app mode (unknown paths fall back to index.html)
|
|
21
|
+
--claim-token <tok> Update a specific anonymous site (overrides .shiply.json)
|
|
22
|
+
--new-site Ignore .shiply.json and create a fresh site
|
|
23
|
+
--key <key> API key (default: $SHIPLY_API_KEY, then ~/.shiply/credentials)
|
|
24
|
+
--anonymous Publish without an API key even if one is saved
|
|
25
|
+
--base <url> API origin (default: ${DEFAULT_BASE})
|
|
26
|
+
--wait (status) poll until the site is ready
|
|
27
|
+
--timeout <seconds> (status --wait) give up after this long (default 300)
|
|
28
|
+
--no-confetti Celebrate quietly
|
|
29
|
+
|
|
30
|
+
\`shiply status\` prints stable machine-readable markers for agents:
|
|
31
|
+
SSL_READY and SITE_READY on success (exit 0); ✖ lines and exit 1 otherwise.
|
|
32
|
+
|
|
33
|
+
With an API key the site is permanent and owned by your account. Without one
|
|
34
|
+
it expires in 24 hours — save the printed claimToken/claimUrl to update or
|
|
35
|
+
claim it later.
|
|
33
36
|
`;
|
|
34
37
|
async function reportReadiness(host, celebrate) {
|
|
35
38
|
const r = await checkReadiness(host);
|
|
@@ -65,6 +68,7 @@ async function main() {
|
|
|
65
68
|
base: { type: 'string' },
|
|
66
69
|
email: { type: 'string' },
|
|
67
70
|
wait: { type: 'boolean' },
|
|
71
|
+
'new-site': { type: 'boolean' },
|
|
68
72
|
timeout: { type: 'string' },
|
|
69
73
|
'no-confetti': { type: 'boolean' },
|
|
70
74
|
help: { type: 'boolean', short: 'h' },
|
|
@@ -80,18 +84,34 @@ async function main() {
|
|
|
80
84
|
case 'update': {
|
|
81
85
|
if (!dir)
|
|
82
86
|
throw new Error(`usage: shiply ${cmd} <dir>`);
|
|
83
|
-
if (cmd === 'update' && !values['claim-token']) {
|
|
84
|
-
throw new Error('shiply update needs --claim-token <token> (printed by the original publish)');
|
|
85
|
-
}
|
|
86
87
|
const apiKey = values.anonymous ? undefined : (values.key ?? (await loadApiKey()));
|
|
88
|
+
// same command, same URL: reuse this directory's site automatically
|
|
89
|
+
const state = await readState(dir);
|
|
90
|
+
let claimToken = values['claim-token'] ?? state?.claimToken;
|
|
91
|
+
let slug = state?.owned && apiKey ? state.slug : undefined;
|
|
92
|
+
if (values['new-site']) {
|
|
93
|
+
claimToken = values['claim-token'];
|
|
94
|
+
slug = undefined;
|
|
95
|
+
}
|
|
96
|
+
if (cmd === 'update' && !claimToken && !slug) {
|
|
97
|
+
throw new Error('nothing to update here — publish first (shiply remembers the site in .shiply.json), or pass --claim-token');
|
|
98
|
+
}
|
|
99
|
+
const updating = Boolean(claimToken || slug);
|
|
87
100
|
const res = await publish(dir, {
|
|
88
101
|
apiKey,
|
|
89
102
|
base: values.base,
|
|
90
103
|
spaMode: values.spa,
|
|
91
|
-
claimToken
|
|
104
|
+
claimToken,
|
|
105
|
+
slug,
|
|
106
|
+
});
|
|
107
|
+
await writeState(dir, {
|
|
108
|
+
slug: res.slug,
|
|
109
|
+
siteUrl: res.siteUrl,
|
|
110
|
+
...(res.claimToken ? { claimToken: res.claimToken } : state?.claimToken && updating ? { claimToken: state.claimToken } : {}),
|
|
111
|
+
owned: !res.anonymous,
|
|
92
112
|
});
|
|
93
113
|
const skipped = res.skipped > 0 ? ` (${res.skipped} unchanged, skipped)` : '';
|
|
94
|
-
console.log(`✔ published ${res.uploaded} file${res.uploaded === 1 ? '' : 's'}${skipped}`);
|
|
114
|
+
console.log(`✔ ${updating ? `updated ${res.slug} in place` : 'published'} — ${res.uploaded} file${res.uploaded === 1 ? '' : 's'}${skipped}`);
|
|
95
115
|
console.log(`\n ${res.siteUrl}\n`);
|
|
96
116
|
// confirm the site actually serves, then celebrate
|
|
97
117
|
try {
|
|
@@ -107,14 +127,14 @@ async function main() {
|
|
|
107
127
|
catch {
|
|
108
128
|
/* serving check is best-effort */
|
|
109
129
|
}
|
|
130
|
+
if (!updating) {
|
|
131
|
+
console.log(` saved .shiply.json — run \`shiply publish ${dir}\` again to UPDATE this same site`);
|
|
132
|
+
console.log(` (gitignore .shiply.json if this folder is public — it can update the site)`);
|
|
133
|
+
}
|
|
110
134
|
if (res.anonymous) {
|
|
111
135
|
console.log(` anonymous site — expires ${res.expiresAt ?? 'in 24h'}`);
|
|
112
136
|
if (res.claimUrl)
|
|
113
|
-
console.log(` claim it (
|
|
114
|
-
if (res.claimToken) {
|
|
115
|
-
console.log(` claimToken (SAVE THIS — shown once): ${res.claimToken}`);
|
|
116
|
-
console.log(` update later: shiply update <dir> --claim-token ${res.claimToken}`);
|
|
117
|
-
}
|
|
137
|
+
console.log(` claim it to KEEP it (free account): ${res.claimUrl}`);
|
|
118
138
|
console.log(` tip: run \`shiply login\` first to publish permanent sites`);
|
|
119
139
|
}
|
|
120
140
|
return;
|
package/dist/publish.js
CHANGED
|
@@ -39,6 +39,7 @@ export async function publish(dir, opts = {}) {
|
|
|
39
39
|
files,
|
|
40
40
|
...(opts.spaMode ? { spaMode: true } : {}),
|
|
41
41
|
...(opts.claimToken ? { claimToken: opts.claimToken } : {}),
|
|
42
|
+
...(opts.slug ? { slug: opts.slug } : {}),
|
|
42
43
|
}),
|
|
43
44
|
});
|
|
44
45
|
await uploadAll(dir, created.upload.uploads);
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export const stateFile = (dir) => join(dir, '.shiply.json');
|
|
4
|
+
export async function readState(dir) {
|
|
5
|
+
try {
|
|
6
|
+
const raw = await readFile(stateFile(dir), 'utf8');
|
|
7
|
+
const parsed = JSON.parse(raw);
|
|
8
|
+
return parsed.slug ? parsed : null;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function writeState(dir, state) {
|
|
15
|
+
await writeFile(stateFile(dir), `${JSON.stringify(state, null, 2)}\n`);
|
|
16
|
+
}
|