shiply-cli 0.21.0 → 0.23.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/dist/bundle.js CHANGED
@@ -18,8 +18,10 @@ const WORKER_EXTERNALS = [
18
18
  ...builtinModules.flatMap((m) => [m, `${m}/*`]),
19
19
  ];
20
20
  /** esbuild-bundle a worker entry into one self-contained ESM module, optionally
21
- * wrapping it (named-fetch → default, and/or asset-first) via a synthetic entry. */
22
- async function bundleWorker(entryAbs, opts = {}) {
21
+ * wrapping it (named-fetch → default, and/or asset-first) via a synthetic entry.
22
+ * Also used by `function deploy` to compile worker.ts → JS and inline npm deps
23
+ * (e.g. `@neondatabase/serverless`) before upload. */
24
+ export async function bundleWorker(entryAbs, opts = {}) {
23
25
  const common = {
24
26
  bundle: true,
25
27
  format: 'esm',
package/dist/functions.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * `parseArgs` in index.ts doesn't have to know about each one. */
6
6
  import { readFile } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
+ import { bundleWorker } from './bundle.js';
8
9
  import { loadApiKey } from './config.js';
9
10
  import { api, resolveBase } from './publish.js';
10
11
  const headers = (apiKey) => ({
@@ -96,19 +97,48 @@ async function deployCmd(argv, inherited) {
96
97
  }
97
98
  const apiKey = await getApiKey(flags, 'function');
98
99
  const base = resolveBase(flags.base);
99
- const lang = flags.ts ? 'ts' : 'js';
100
- const filename = lang === 'ts' ? 'worker.ts' : 'worker.js';
101
- let source;
100
+ // Collect repeatable `--secret NAME=VALUE` set on the worker as part of this
101
+ // deploy so first-time setup doesn't hit "deploy a function first".
102
+ const secrets = [];
103
+ for (let i = 0; i < argv.length; i++) {
104
+ let pair;
105
+ if (argv[i] === '--secret') {
106
+ pair = argv[i + 1];
107
+ i++;
108
+ }
109
+ else if (argv[i].startsWith('--secret=')) {
110
+ pair = argv[i].slice('--secret='.length);
111
+ }
112
+ if (pair) {
113
+ const eq = pair.indexOf('=');
114
+ if (eq < 1)
115
+ throw new Error(`--secret must be NAME=VALUE (got "${pair}")`);
116
+ secrets.push({ name: pair.slice(0, eq), value: pair.slice(eq + 1) });
117
+ }
118
+ }
119
+ const filename = flags.ts ? 'worker.ts' : 'worker.js';
120
+ const entryAbs = join(process.cwd(), filename);
121
+ // Pre-read for a friendly "file not found" message (esbuild's is cryptic).
102
122
  try {
103
- source = await readFile(join(process.cwd(), filename), 'utf8');
123
+ await readFile(entryAbs, 'utf8');
104
124
  }
105
125
  catch (e) {
106
126
  throw new Error(`couldn't read ${filename} from ${process.cwd()} — ${e.message}\n hint: cd into the directory containing your ${filename}, or pass --ts if the file is worker.ts`);
107
127
  }
128
+ // Compile (TS→JS) + bundle npm deps client-side via esbuild so imports like
129
+ // `@neondatabase/serverless` work; cloudflare:*/node:* stay external. We then
130
+ // upload plain JS — the platform can't run esbuild on workerd.
131
+ let source;
132
+ try {
133
+ source = await bundleWorker(entryAbs);
134
+ }
135
+ catch (e) {
136
+ throw new Error(`couldn't bundle ${filename}: ${e.message}`);
137
+ }
108
138
  const row = await api(`${base}/api/v1/sites/${slug}/function`, {
109
139
  method: 'POST',
110
140
  headers: headers(apiKey),
111
- body: JSON.stringify({ source, lang }),
141
+ body: JSON.stringify({ source, lang: 'js', ...(secrets.length ? { secrets } : {}) }),
112
142
  });
113
143
  if (flags.json) {
114
144
  console.log(JSON.stringify(row, null, 2));
@@ -117,7 +147,9 @@ async function deployCmd(argv, inherited) {
117
147
  console.log(`✔ deployed worker for ${slug}`);
118
148
  console.log(` script: ${row.cfScriptName}`);
119
149
  console.log(` version: ${row.version.slice(0, 12)}`);
120
- console.log(` source: ${filename} (${lang})`);
150
+ console.log(` source: ${filename} (bundled → js)`);
151
+ if (secrets.length)
152
+ console.log(` secrets: set ${secrets.map((s) => s.name).join(', ')}`);
121
153
  console.log(` routes: requests to https://${slug}.shiply.now/* now hit your worker first`);
122
154
  }
123
155
  async function getFnCmd(argv, inherited) {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { createInterface } from 'node:readline/promises';
5
5
  import { fileURLToPath } from 'node:url';
@@ -318,6 +318,17 @@ async function main() {
318
318
  if (resolved.hint && publishDir !== dir && !bareJsonFlag) {
319
319
  console.log(`✔ ${resolved.hint.framework} project detected — publishing ${publishDir}${resolved.hint.spa ? ' with SPA mode' : ''}`);
320
320
  }
321
+ // A bare worker.{ts,js} at the publish root ships as a STATIC asset here
322
+ // (downloadable source, never runs). Warn — it deploys separately as a
323
+ // per-site Worker via `shiply function deploy`.
324
+ if (!bareJsonFlag) {
325
+ for (const wf of ['worker.ts', 'worker.js']) {
326
+ if (existsSync(join(publishDir, wf))) {
327
+ console.log(`⚠ ${wf} will be uploaded as a STATIC file (its source is downloadable) and will NOT run as a worker.\n` +
328
+ ` Deploy it as a per-site Worker instead: shiply function deploy <slug>${wf.endsWith('.ts') ? ' --ts' : ''}`);
329
+ }
330
+ }
331
+ }
321
332
  }
322
333
  try {
323
334
  // same command, same URL: reuse this directory's site automatically
@@ -337,6 +348,10 @@ async function main() {
337
348
  throw new Error('nothing to update here — publish first (shiply remembers the site in .shiply.json), or pass --claim-token');
338
349
  }
339
350
  const updating = Boolean(claimToken || slug);
351
+ // Send the stable siteId so a publish self-heals after a handle rename
352
+ // left state.slug stale. Only for the normal state-based update path —
353
+ // never with --as (alias has its own slug) or --new-site.
354
+ const siteId = !aliasName && !values['new-site'] && state?.owned && apiKey ? state?.siteId : undefined;
340
355
  // Auto-attach a previously-created database on publish. Anonymous
341
356
  // publishes can't attach (no auth subject) — silently skipped server-side.
342
357
  const attachDatabaseId = state?.databaseId && apiKey ? state.databaseId : undefined;
@@ -350,6 +365,7 @@ async function main() {
350
365
  spaMode: spa,
351
366
  claimToken,
352
367
  slug,
368
+ siteId,
353
369
  attachDatabaseId,
354
370
  previewBranchDbId,
355
371
  // Group under a client (owned publishes only). --client "<name|email>"
package/dist/publish.js CHANGED
@@ -40,6 +40,7 @@ export async function publish(dir, opts = {}) {
40
40
  ...(opts.spaMode ? { spaMode: true } : {}),
41
41
  ...(opts.claimToken ? { claimToken: opts.claimToken } : {}),
42
42
  ...(opts.slug ? { slug: opts.slug } : {}),
43
+ ...(opts.siteId ? { siteId: opts.siteId } : {}),
43
44
  ...(opts.attachDatabaseId ? { attachDatabaseId: opts.attachDatabaseId } : {}),
44
45
  ...(opts.previewBranchDbId ? { previewBranchDbId: opts.previewBranchDbId } : {}),
45
46
  ...(opts.client ? { client: opts.client } : {}),
package/package.json CHANGED
@@ -1,42 +1,42 @@
1
- {
2
- "name": "shiply-cli",
3
- "version": "0.21.0",
4
- "description": "Publish static sites to shiply.now from the command line instant web hosting for agents.",
5
- "license": "MIT",
6
- "type": "module",
7
- "bin": {
8
- "shiply": "dist/index.js"
9
- },
10
- "files": [
11
- "dist",
12
- "skill",
13
- "README.md"
14
- ],
15
- "scripts": {
16
- "build": "tsc -p tsconfig.build.json",
17
- "prepublishOnly": "pnpm build",
18
- "test": "vitest run",
19
- "typecheck": "tsc --noEmit"
20
- },
21
- "engines": {
22
- "node": ">=18"
23
- },
24
- "keywords": [
25
- "shiply",
26
- "hosting",
27
- "static",
28
- "deploy",
29
- "agents",
30
- "publish"
31
- ],
32
- "homepage": "https://shiply.now",
33
- "devDependencies": {
34
- "@types/node": "^22.15.0",
35
- "typescript": "^5.8.0",
36
- "vitest": "^3.1.0"
37
- },
38
- "dependencies": {
39
- "esbuild": "^0.25.12",
40
- "smol-toml": "^1.7.0"
41
- }
42
- }
1
+ {
2
+ "name": "shiply-cli",
3
+ "version": "0.23.0",
4
+ "description": "Publish static sites to shiply.now from the command line \u00e2\u20ac\u201d instant web hosting for agents.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "shiply": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "skill",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.build.json",
17
+ "prepublishOnly": "pnpm build",
18
+ "test": "vitest run",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "keywords": [
25
+ "shiply",
26
+ "hosting",
27
+ "static",
28
+ "deploy",
29
+ "agents",
30
+ "publish"
31
+ ],
32
+ "homepage": "https://shiply.now",
33
+ "devDependencies": {
34
+ "@types/node": "^22.15.0",
35
+ "typescript": "^5.8.0",
36
+ "vitest": "^3.1.0"
37
+ },
38
+ "dependencies": {
39
+ "esbuild": "^0.25.12",
40
+ "smol-toml": "^1.7.0"
41
+ }
42
+ }
package/skill/SKILL.md CHANGED
@@ -26,7 +26,7 @@ description: "shiply is the production backend for anything an agent builds —
26
26
 
27
27
  **What it is.** Not static hosting. shiply takes whatever you build — a static page or a full-stack app in any framework — and gives it a live URL, a custom domain, a SQL database, server functions, email (send + receive), and a marketplace to sell it. One call. No account needed to start: anonymous sites are live immediately, last 24 hours, and can be claimed to keep.
28
28
 
29
- **Why use it.** Ship a complete, working product without stitching together a host + database + email + registrar + payments — it's one agent-native platform (MCP / CLI / REST parity). Hard-ceiling pricing ($0/$8/$24, no usage meter), so a spike or DDoS can never become a surprise bill for the owner.
29
+ **Why use it.** Ship a complete, working product without stitching together a host + database + email + registrar + payments — it's one agent-native platform (MCP / CLI / REST parity). Hard-ceiling pricing ($0/$8/$24, plus a $49 Studio tier for client delivery — no usage meter on any tier), so a spike or DDoS can never become a surprise bill for the owner.
30
30
 
31
31
  **How (quickstart).** `shiply publish ./dir` (CLI) — or POST /api/v1/publish → PUT each file → POST finalize — returns a live URL + claimToken.
32
32