@solcreek/cli 0.4.4 → 0.4.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @solcreek/cli
2
2
 
3
+ ## 0.4.5
4
+
5
+ ### Features
6
+
7
+ - **`creek deploy --dry-run`** — new flag that reports a plan without
8
+ executing: resolved config source, detected framework, build command,
9
+ build output, bindings, cron/queue triggers, auth status, and the
10
+ target type (sandbox vs production). No network calls, no file
11
+ uploads, no ToS prompt, no build. Pair with `--json` for a
12
+ machine-readable plan. Safe to call from an AI coding agent that
13
+ wants to understand `creek deploy` behavior before running it.
14
+ Unsupported modes (`--template`, `--from-github`, repo-url) short-
15
+ circuit with a clear "not yet supported" message.
16
+
17
+ - **Static-site sandbox fast path** — `creek deploy` in a directory
18
+ that contains only `index.html` (no `package.json`) now works. The
19
+ SDK falls back to the `index.html` source and `deploySandbox`
20
+ recognizes that case and delegates straight to `deployDirectory`,
21
+ which uploads the cwd as-is. Previously crashed with an ENOENT on
22
+ package.json. The simplest possible onboarding now works end-to-end:
23
+
24
+ mkdir hello && cd hello
25
+ echo '<h1>Hi</h1>' > index.html
26
+ npx creek deploy
27
+
28
+ - **Astro framework support** — any Astro 3+ project (SPA, SSG, MDX,
29
+ content collections) auto-detects and builds via `astro build`.
30
+ Tested end-to-end against Astro 6 + Tailwind 4 + sharp image
31
+ optimization (`jiseeeh/serene-ink` blog theme). Via the existing
32
+ repo-URL flow, `npx creek deploy https://github.com/user/astro-theme`
33
+ now works with zero Creek-side configuration.
34
+
35
+ - **Richer `creek deploy --help`** — `meta.description` and every arg
36
+ description rewritten to spell out sandbox-vs-production behavior,
37
+ auto-detection chain, safety notes around `--dry-run`, and example
38
+ invocations. Primary audience is AI agents reading `--help` output
39
+ on a cold paste, which shows up concretely as better self-description
40
+ in the first tool call.
41
+
42
+ ### Fixes
43
+
44
+ - `NO_PROJECT_BREADCRUMBS`, the sandbox-status not-found hint, and
45
+ deploy.ts error paths now point to
46
+ `creek deploy --template landing` instead of the non-existent
47
+ `--demo` flag. Matching doc scrub across README, docs, llms.txt,
48
+ and the pricing page.
49
+
3
50
  ## 0.4.4
4
51
 
5
52
  ### Features
package/README.md CHANGED
@@ -6,7 +6,7 @@ Deploy to the edge in seconds. No account required.
6
6
  [![License](https://img.shields.io/badge/license-Apache%202.0-green)](https://github.com/solcreek/creek/blob/main/LICENSE)
7
7
 
8
8
  ```bash
9
- npx creek deploy --demo
9
+ npx creek deploy --template landing
10
10
  # Live in 8.3s -> https://a1b2c3d4.creeksandbox.com
11
11
  ```
12
12
 
@@ -28,12 +28,14 @@ npx creek deploy
28
28
 
29
29
  ## Quick Start
30
30
 
31
- ### Deploy a demo (no account needed)
31
+ ### Start from a template (no account needed)
32
32
 
33
33
  ```bash
34
- creek deploy --demo
34
+ creek deploy --template landing
35
35
  ```
36
36
 
37
+ Clones a ready-made Vite + React landing page, builds it, and deploys it to a 60-minute sandbox URL — all in one command. The code is yours to edit.
38
+
37
39
  ### Deploy your project
38
40
 
39
41
  ```bash
@@ -61,7 +63,8 @@ creek deploy https://github.com/user/repo/tree/main/packages/app
61
63
  | Command | Description |
62
64
  |---------|-------------|
63
65
  | `creek deploy [dir]` | Deploy project, directory, or repo URL |
64
- | `creek deploy --demo` | Deploy a sample site instantly |
66
+ | `creek deploy --template <name>` | Clone + build + deploy a named template |
67
+ | `creek deploy --dry-run` | Show the deploy plan without executing (agent-safe) |
65
68
  | `creek projects` | List your projects |
66
69
  | `creek deployments` | List deployments for a project |
67
70
  | `creek status` | Show current project status |
@@ -154,7 +157,7 @@ Creek auto-detects and configures the build for:
154
157
 
155
158
  | Mode | Trigger | Auth Required | TTL |
156
159
  |------|---------|:-------------:|-----|
157
- | **Demo** | `--demo` flag | No | 60 min |
160
+ | **Template** | `--template <name>` | No | 60 min |
158
161
  | **Sandbox** | No `creek.toml` | No | 60 min |
159
162
  | **Production** | Has `creek.toml` + token | Yes | Permanent |
160
163
 
@@ -44,6 +44,11 @@ export declare const deployCommand: import("citty").CommandDef<{
44
44
  description: string;
45
45
  default: false;
46
46
  };
47
+ "dry-run": {
48
+ type: "boolean";
49
+ description: string;
50
+ default: false;
51
+ };
47
52
  }>;
48
53
  export interface CliDeployment {
49
54
  id: string;
@@ -30,51 +30,200 @@ function assetSummary(fileList) {
30
30
  .map(([ext, n]) => `${n} ${ext}`);
31
31
  return parts.join(", ");
32
32
  }
33
+ /**
34
+ * Dry-run handler: describes what `creek deploy` would do without
35
+ * executing. No network calls, no file uploads, no builds, no ToS
36
+ * prompts. Safe to call from an AI agent that wants to understand
37
+ * the deploy plan before running it.
38
+ *
39
+ * Always exits 0 — a dry-run is "successful" even when no project is
40
+ * found, because the goal is to report state, not enforce it.
41
+ */
42
+ async function dryRunPlan(cwd, args, jsonMode) {
43
+ // Unsupported modes — report and return cleanly
44
+ const unsupportedMode = args.template ? "template"
45
+ : args["from-github"] ? "from-github"
46
+ : (typeof args.dir === "string" && isRepoUrl(args.dir)) ? "repo-url"
47
+ : null;
48
+ if (unsupportedMode) {
49
+ if (jsonMode) {
50
+ jsonOutput({
51
+ mode: "dry-run",
52
+ supported: false,
53
+ unsupportedMode,
54
+ message: `Dry-run is not yet supported for --${unsupportedMode} deploys`,
55
+ hint: `Run without --dry-run to execute, or inspect the command docs with --help`,
56
+ }, 0, []);
57
+ return;
58
+ }
59
+ consola.log(`\n \x1b[2m[dry-run]\x1b[0m Dry-run is not yet supported for --${unsupportedMode} deploys.\n`);
60
+ consola.log(` Run without --dry-run to execute, or inspect the command docs with --help.\n`);
61
+ return;
62
+ }
63
+ // Main case: resolve config in cwd
64
+ let resolved = null;
65
+ try {
66
+ resolved = resolveConfig(cwd);
67
+ }
68
+ catch (err) {
69
+ if (!(err instanceof ConfigNotFoundError))
70
+ throw err;
71
+ }
72
+ const token = getToken();
73
+ const authenticated = !!token;
74
+ // If no config, check build-output fallback
75
+ let buildOutputFallback = null;
76
+ if (!resolved) {
77
+ for (const dir of ["dist", "build", "out", ".output/public"]) {
78
+ if (existsSync(resolve(cwd, dir))) {
79
+ buildOutputFallback = dir;
80
+ break;
81
+ }
82
+ }
83
+ }
84
+ const wouldDeploy = !!(resolved || buildOutputFallback);
85
+ const targetType = authenticated ? "production" : "sandbox";
86
+ const bindings = resolved
87
+ ? resolvedConfigToBindingRequirements(resolved).map((b) => ({
88
+ name: b.bindingName,
89
+ type: b.type,
90
+ }))
91
+ : [];
92
+ const plan = {
93
+ mode: "dry-run",
94
+ supported: true,
95
+ cwd,
96
+ authenticated,
97
+ target: {
98
+ type: targetType,
99
+ description: targetType === "production"
100
+ ? "Your team's production slot (permanent URL via creek.toml)"
101
+ : "Free 60-minute sandbox (no signup required)",
102
+ },
103
+ config: resolved
104
+ ? {
105
+ source: resolved.source,
106
+ projectName: resolved.projectName,
107
+ framework: resolved.framework,
108
+ buildCommand: resolved.buildCommand,
109
+ buildOutput: resolved.buildOutput,
110
+ cron: resolved.cron,
111
+ queue: resolved.queue,
112
+ }
113
+ : null,
114
+ buildOutputFallback,
115
+ bindings,
116
+ wouldDeploy,
117
+ sideEffects: {
118
+ networkCalls: false,
119
+ fileUploads: false,
120
+ buildExecuted: false,
121
+ tosPromptShown: false,
122
+ },
123
+ nextStep: wouldDeploy
124
+ ? "Run without --dry-run to execute: npx creek deploy"
125
+ : "No project config or build output found. Run `creek init` or `npm create vite@latest` first.",
126
+ };
127
+ if (jsonMode) {
128
+ jsonOutput(plan, 0, []);
129
+ return;
130
+ }
131
+ // Human-readable plan
132
+ consola.log("\n \x1b[2m[dry-run]\x1b[0m No deploy will execute. No network calls. No uploads.\n");
133
+ consola.log(` cwd: ${cwd}`);
134
+ consola.log(` Auth status: ${authenticated ? "✓ signed in" : "✗ not signed in"}`);
135
+ consola.log(` Deploy target: ${targetType} — ${plan.target.description}`);
136
+ if (resolved) {
137
+ consola.log(` Detected: ${formatDetectionSummary(resolved)}`);
138
+ consola.log(` Project name: ${resolved.projectName}`);
139
+ if (resolved.framework) {
140
+ consola.log(` Framework: ${resolved.framework}`);
141
+ }
142
+ consola.log(` Build command: ${resolved.buildCommand}`);
143
+ consola.log(` Build output: ${resolved.buildOutput}`);
144
+ if (bindings.length > 0) {
145
+ consola.log(` Bindings: ${bindings.map((b) => `${b.name} (${b.type})`).join(", ")}`);
146
+ }
147
+ if (resolved.cron.length > 0) {
148
+ consola.log(` Cron triggers: ${resolved.cron.join(", ")}`);
149
+ }
150
+ if (resolved.queue) {
151
+ consola.log(` Queue trigger: enabled`);
152
+ }
153
+ if (resolved.unsupportedBindings.length > 0) {
154
+ consola.log(` Unsupported: ${resolved.unsupportedBindings
155
+ .map((b) => `${b.name} (${b.type})`)
156
+ .join(", ")} — would be skipped`);
157
+ }
158
+ }
159
+ else if (buildOutputFallback) {
160
+ consola.log(` Detected: prebuilt assets in ${buildOutputFallback}/ (no creek.toml, no wrangler config)`);
161
+ }
162
+ else {
163
+ consola.log(` Detected: nothing — no creek.toml, no wrangler config, no dist/build/out/.output/public/`);
164
+ }
165
+ consola.log(`\n Next step: ${plan.nextStep}\n`);
166
+ }
33
167
  export const deployCommand = defineCommand({
34
168
  meta: {
35
169
  name: "deploy",
36
- description: "Deploy the current project to Creek",
170
+ description: "Deploy the current project to Creek. Signed-in: deploys to your team's production slot. Not signed in: creates a free 60-minute sandbox URL (no signup). Auto-detects framework from creek.toml, wrangler files, package.json, or index.html. Safe to run from an AI coding agent — use --dry-run first to inspect the plan without executing.",
37
171
  },
38
172
  args: {
39
173
  dir: {
40
174
  type: "positional",
41
- description: "Directory to deploy (default: current directory)",
175
+ description: "Directory to deploy (default: current directory). Also accepts a GitHub repo URL to clone-and-deploy.",
42
176
  required: false,
43
177
  },
44
178
  "skip-build": {
45
179
  type: "boolean",
46
- description: "Skip the build step",
180
+ description: "Skip the build step — upload existing build output as-is. Useful when CI already built the artifact.",
181
+ default: false,
182
+ },
183
+ "dry-run": {
184
+ type: "boolean",
185
+ description: "Show what would be deployed without doing it: resolved config, detected framework, bindings, target URL, auth status. No network calls, no file uploads. Pair with --json for machine-readable plan output. Safe for agents.",
47
186
  default: false,
48
187
  },
49
188
  ...globalArgs,
50
189
  template: {
51
190
  type: "string",
52
- description: "Deploy a template (e.g., landing, blog, todo)",
191
+ description: "Deploy a named template (e.g., landing, blog, todo). Combine with --data to pass template params.",
53
192
  required: false,
54
193
  },
55
194
  data: {
56
195
  type: "string",
57
- description: "JSON data for template params (used with --template)",
196
+ description: "JSON string of template parameters (used with --template). Example: --data '{\"title\":\"Hello\"}'",
58
197
  required: false,
59
198
  },
60
199
  path: {
61
200
  type: "string",
62
- description: "Subdirectory within repo to deploy (for monorepos)",
201
+ description: "Subdirectory within the repo to deploy, for monorepos. Example: --path apps/web",
63
202
  required: false,
64
203
  },
65
204
  "from-github": {
66
205
  type: "boolean",
67
- description: "Skip local build; trigger a deploy of the latest commit on the project's production branch via its GitHub connection",
206
+ description: "Skip local build; trigger a remote deploy of the latest commit on the project's production branch via its GitHub connection.",
68
207
  default: false,
69
208
  },
70
209
  project: {
71
210
  type: "string",
72
- description: "Target project slug or UUID (required with --from-github when not run inside a project directory)",
211
+ description: "Target project slug or UUID. Required with --from-github when not run inside a project directory.",
73
212
  required: false,
74
213
  },
75
214
  },
76
215
  async run({ args }) {
77
216
  const jsonMode = resolveJsonMode(args);
217
+ // --- Dry-run short-circuit ---
218
+ // No ToS prompt, no network, no file uploads, no builds. Pure config
219
+ // resolution + plan output. Safe to call from an AI agent that wants
220
+ // to understand what `creek deploy` would do before running it.
221
+ if (args["dry-run"]) {
222
+ const dryCwd = args.dir && typeof args.dir === "string" && !isRepoUrl(args.dir)
223
+ ? resolve(args.dir)
224
+ : process.cwd();
225
+ return await dryRunPlan(dryCwd, args, jsonMode);
226
+ }
78
227
  // --- Ensure ToS accepted ---
79
228
  const autoConfirm = shouldAutoConfirm(args);
80
229
  const tos = await ensureTosAccepted(autoConfirm);
@@ -490,9 +639,39 @@ async function deployDirectory(dir, jsonMode, tos) {
490
639
  // Sandbox deploy — auto-detect framework, build, deploy
491
640
  // ============================================================================
492
641
  async function deploySandbox(cwd, skipBuild, jsonMode = false, resolved, tos) {
493
- const framework = resolved?.framework ?? detectFramework(JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8")));
642
+ // Static-site fast path: when resolveConfig fell back to index.html, the cwd
643
+ // has no build step and may have no package.json. Delegate to deployDirectory
644
+ // which just uploads the cwd as-is. This handles the simplest possible
645
+ // onboarding:
646
+ // mkdir test && cd test
647
+ // echo '<h1>Hi</h1>' > index.html
648
+ // npx creek deploy
649
+ if (resolved?.source === "index.html") {
650
+ return deployDirectory(cwd, jsonMode, tos);
651
+ }
652
+ // Framework path: package.json should exist because resolveConfig picked a
653
+ // framework from package.json, wrangler files, or creek.toml. Guard anyway
654
+ // so we fail with a helpful message instead of an ENOENT stack trace.
655
+ const pkgJsonPath = join(cwd, "package.json");
656
+ if (!existsSync(pkgJsonPath)) {
657
+ const message = `Expected package.json in ${cwd} but none found.`;
658
+ if (jsonMode) {
659
+ jsonOutput({
660
+ ok: false,
661
+ error: "no_package_json",
662
+ message,
663
+ hint: "Run `npx creek deploy ./dist` to deploy a prebuilt directory, or `npx creek deploy --template landing` to start from a template.",
664
+ }, 1, NO_PROJECT_BREADCRUMBS);
665
+ return;
666
+ }
667
+ consola.error(message);
668
+ consola.info(" Run `npx creek deploy ./dist` to deploy a prebuilt directory instead,");
669
+ consola.info(" or `npx creek deploy --template landing` to start from a template.");
670
+ process.exit(1);
671
+ }
672
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
673
+ const framework = resolved?.framework ?? detectFramework(pkg);
494
674
  // Detect Next.js mode (static vs opennext SSR)
495
- const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
496
675
  const nextjsMode = framework === "nextjs" ? detectNextjsMode(pkg, cwd) : null;
497
676
  const monorepo = detectMonorepo(cwd);
498
677
  section("Detect");
@@ -33,7 +33,7 @@ async function sandboxStatus(sandboxId, jsonMode) {
33
33
  if (!res.ok) {
34
34
  if (jsonMode)
35
35
  jsonOutput({ ok: false, error: "not_found", message: "Sandbox not found" }, 1, [
36
- { command: "creek deploy --demo", description: "Deploy a new sandbox" },
36
+ { command: "creek deploy --template landing", description: "Deploy a new sandbox from a template" },
37
37
  ]);
38
38
  consola.error("Sandbox not found. It may have expired.");
39
39
  process.exit(1);
@@ -50,6 +50,6 @@ export const AUTH_BREADCRUMBS = [
50
50
  ];
51
51
  export const NO_PROJECT_BREADCRUMBS = [
52
52
  { command: "creek init", description: "Initialize creek.toml in current directory" },
53
- { command: "creek deploy --demo", description: "Deploy a sample site to try Creek" },
53
+ { command: "creek deploy --template landing", description: "Start from a ready-made Vite + React landing page" },
54
54
  ];
55
55
  //# sourceMappingURL=output.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solcreek/cli",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "CLI for the Creek deployment platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,7 +34,7 @@
34
34
  "miniflare": "^4.20260317.3",
35
35
  "smol-toml": "^1.3.1",
36
36
  "ws": "^8.20.0",
37
- "@solcreek/sdk": "0.4.1"
37
+ "@solcreek/sdk": "0.4.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@testing-library/dom": "^10.4.1",