@solcreek/cli 0.4.4 → 0.4.6
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 +77 -0
- package/README.md +8 -5
- package/dist/commands/deploy.d.ts +5 -0
- package/dist/commands/deploy.js +189 -10
- package/dist/commands/status.js +1 -1
- package/dist/dev/worker-runner.d.ts +10 -0
- package/dist/dev/worker-runner.js +92 -5
- package/dist/utils/output.js +1 -1
- package/dist/utils/repo-url.d.ts +5 -2
- package/dist/utils/repo-url.js +12 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# @solcreek/cli
|
|
2
2
|
|
|
3
|
+
## 0.4.6
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **`creek deploy gh:owner/repo` shorthand** — a shorter alias for
|
|
8
|
+
`https://github.com/owner/repo`. Matches the GitHub CLI convention.
|
|
9
|
+
`gh:`, `gl:` (GitLab), and `bb:` (Bitbucket) are now all registered
|
|
10
|
+
alongside the longer `github:` / `gitlab:` / `bitbucket:` forms.
|
|
11
|
+
Example: `npx creek deploy gh:jiseeeh/serene-ink`.
|
|
12
|
+
|
|
13
|
+
### Fixes / DX
|
|
14
|
+
|
|
15
|
+
- **Install size cut from ~170MB to ~25MB** by moving `miniflare` out of
|
|
16
|
+
runtime dependencies entirely. Miniflare plus its transitive deps
|
|
17
|
+
(workerd, sharp) was adding ~146MB to every `npm install creek` — a
|
|
18
|
+
pure penalty on the `creek deploy` flow, which never touches
|
|
19
|
+
miniflare. `miniflare` is now listed in `devDependencies` only, so
|
|
20
|
+
the published package is free of it. First-time `npx creek deploy`
|
|
21
|
+
goes from ~20s install to ~3–5s, and the postinstall warnings that
|
|
22
|
+
used to appear on systems without build tools (sharp's node-gyp
|
|
23
|
+
fallback) are gone entirely.
|
|
24
|
+
|
|
25
|
+
- **`creek dev` loads miniflare from multiple locations** via
|
|
26
|
+
`createRequire` — the user's current project (`npm install --save-dev
|
|
27
|
+
miniflare`), a creek-managed cache directory at `~/.creek/deps`, or
|
|
28
|
+
the global npm root (`npm install -g miniflare`). If none of those
|
|
29
|
+
have it, a jargon-free error explains what to install and where.
|
|
30
|
+
Users who only want to deploy never see this error because
|
|
31
|
+
`creek deploy` doesn't load the local runtime at all.
|
|
32
|
+
|
|
33
|
+
## 0.4.5
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
- **`creek deploy --dry-run`** — new flag that reports a plan without
|
|
38
|
+
executing: resolved config source, detected framework, build command,
|
|
39
|
+
build output, bindings, cron/queue triggers, auth status, and the
|
|
40
|
+
target type (sandbox vs production). No network calls, no file
|
|
41
|
+
uploads, no ToS prompt, no build. Pair with `--json` for a
|
|
42
|
+
machine-readable plan. Safe to call from an AI coding agent that
|
|
43
|
+
wants to understand `creek deploy` behavior before running it.
|
|
44
|
+
Unsupported modes (`--template`, `--from-github`, repo-url) short-
|
|
45
|
+
circuit with a clear "not yet supported" message.
|
|
46
|
+
|
|
47
|
+
- **Static-site sandbox fast path** — `creek deploy` in a directory
|
|
48
|
+
that contains only `index.html` (no `package.json`) now works. The
|
|
49
|
+
SDK falls back to the `index.html` source and `deploySandbox`
|
|
50
|
+
recognizes that case and delegates straight to `deployDirectory`,
|
|
51
|
+
which uploads the cwd as-is. Previously crashed with an ENOENT on
|
|
52
|
+
package.json. The simplest possible onboarding now works end-to-end:
|
|
53
|
+
|
|
54
|
+
mkdir hello && cd hello
|
|
55
|
+
echo '<h1>Hi</h1>' > index.html
|
|
56
|
+
npx creek deploy
|
|
57
|
+
|
|
58
|
+
- **Astro framework support** — any Astro 3+ project (SPA, SSG, MDX,
|
|
59
|
+
content collections) auto-detects and builds via `astro build`.
|
|
60
|
+
Tested end-to-end against Astro 6 + Tailwind 4 + sharp image
|
|
61
|
+
optimization (`jiseeeh/serene-ink` blog theme). Via the existing
|
|
62
|
+
repo-URL flow, `npx creek deploy https://github.com/user/astro-theme`
|
|
63
|
+
now works with zero Creek-side configuration.
|
|
64
|
+
|
|
65
|
+
- **Richer `creek deploy --help`** — `meta.description` and every arg
|
|
66
|
+
description rewritten to spell out sandbox-vs-production behavior,
|
|
67
|
+
auto-detection chain, safety notes around `--dry-run`, and example
|
|
68
|
+
invocations. Primary audience is AI agents reading `--help` output
|
|
69
|
+
on a cold paste, which shows up concretely as better self-description
|
|
70
|
+
in the first tool call.
|
|
71
|
+
|
|
72
|
+
### Fixes
|
|
73
|
+
|
|
74
|
+
- `NO_PROJECT_BREADCRUMBS`, the sandbox-status not-found hint, and
|
|
75
|
+
deploy.ts error paths now point to
|
|
76
|
+
`creek deploy --template landing` instead of the non-existent
|
|
77
|
+
`--demo` flag. Matching doc scrub across README, docs, llms.txt,
|
|
78
|
+
and the pricing page.
|
|
79
|
+
|
|
3
80
|
## 0.4.4
|
|
4
81
|
|
|
5
82
|
### Features
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Deploy to the edge in seconds. No account required.
|
|
|
6
6
|
[](https://github.com/solcreek/creek/blob/main/LICENSE)
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
npx creek deploy --
|
|
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
|
-
###
|
|
31
|
+
### Start from a template (no account needed)
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
creek deploy --
|
|
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 --
|
|
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
|
-
| **
|
|
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;
|
package/dist/commands/deploy.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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");
|
package/dist/commands/status.js
CHANGED
|
@@ -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 --
|
|
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);
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { BindingDeclaration } from "@solcreek/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by `creek dev` when no local runtime package can be resolved from
|
|
4
|
+
* the user's project, global npm root, or creek's dep cache. The message
|
|
5
|
+
* is deliberately jargon-free: it talks about "a local runtime" instead of
|
|
6
|
+
* naming miniflare in the explanation, and only mentions the package name
|
|
7
|
+
* as part of the exact npm command the user needs to run.
|
|
8
|
+
*/
|
|
9
|
+
export declare class MiniflareNotInstalledError extends Error {
|
|
10
|
+
constructor();
|
|
11
|
+
}
|
|
2
12
|
export interface WorkerRunnerOptions {
|
|
3
13
|
/** User's worker entry point (e.g. "worker/index.ts"). */
|
|
4
14
|
entryPoint: string;
|
|
@@ -1,11 +1,93 @@
|
|
|
1
|
-
// Worker execution for `creek dev` — bundles user code and runs it via
|
|
1
|
+
// Worker execution for `creek dev` — bundles user code and runs it via
|
|
2
|
+
// Miniflare for local D1 / KV / R2 / Queues / Durable Objects simulation.
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
+
// `miniflare` is intentionally NOT in @solcreek/cli's runtime dependencies —
|
|
5
|
+
// it (plus its transitive deps workerd and sharp) adds ~146MB to every
|
|
6
|
+
// install of `creek`. Since `creek deploy` never touches miniflare, it's
|
|
7
|
+
// a pure penalty on the aha-moment flow: paste a command, see a live URL.
|
|
8
|
+
//
|
|
9
|
+
// Instead, `creek dev` resolves miniflare at runtime from the caller's
|
|
10
|
+
// project (npm install --save-dev miniflare in their own package.json),
|
|
11
|
+
// the global npm root, or a creek-managed cache dir. If none of those
|
|
12
|
+
// have miniflare, we throw a zero-jargon error that tells the user
|
|
13
|
+
// exactly one command to run.
|
|
4
14
|
import { writeFileSync, mkdirSync } from "node:fs";
|
|
5
15
|
import { join, resolve } from "node:path";
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { execSync } from "node:child_process";
|
|
6
19
|
import { context } from "esbuild";
|
|
7
|
-
import { Miniflare } from "miniflare";
|
|
8
20
|
import { generateWorkerWrapper } from "../utils/worker-bundle.js";
|
|
21
|
+
/**
|
|
22
|
+
* Thrown by `creek dev` when no local runtime package can be resolved from
|
|
23
|
+
* the user's project, global npm root, or creek's dep cache. The message
|
|
24
|
+
* is deliberately jargon-free: it talks about "a local runtime" instead of
|
|
25
|
+
* naming miniflare in the explanation, and only mentions the package name
|
|
26
|
+
* as part of the exact npm command the user needs to run.
|
|
27
|
+
*/
|
|
28
|
+
export class MiniflareNotInstalledError extends Error {
|
|
29
|
+
constructor() {
|
|
30
|
+
super([
|
|
31
|
+
"`creek dev` needs a local runtime to simulate the edge environment,",
|
|
32
|
+
"and it isn't installed yet.",
|
|
33
|
+
"",
|
|
34
|
+
" One-time setup — run this in your project directory:",
|
|
35
|
+
" npm install --save-dev miniflare",
|
|
36
|
+
"",
|
|
37
|
+
" This downloads ~150MB but only needs to happen once per project.",
|
|
38
|
+
" `creek deploy` already works without it — you can skip this step",
|
|
39
|
+
" entirely if you only want to deploy.",
|
|
40
|
+
].join("\n"));
|
|
41
|
+
this.name = "MiniflareNotInstalledError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Try to load miniflare from several plausible locations, in order of
|
|
46
|
+
* preference:
|
|
47
|
+
*
|
|
48
|
+
* 1. The user's current project (npm install --save-dev miniflare)
|
|
49
|
+
* 2. Creek-managed dep cache at ~/.creek/deps/miniflare (for future
|
|
50
|
+
* auto-install support — not wired up yet but already searched here
|
|
51
|
+
* so a future `creek dev install` command can drop files there)
|
|
52
|
+
* 3. Global npm root (for users who `npm install -g miniflare`)
|
|
53
|
+
*
|
|
54
|
+
* Each location is attempted with a fresh `createRequire` rooted at a
|
|
55
|
+
* dummy file in that directory, then `require.resolve("miniflare")` is
|
|
56
|
+
* used to find the package. If all three fail, throws
|
|
57
|
+
* MiniflareNotInstalledError with a zero-jargon explanation.
|
|
58
|
+
*/
|
|
59
|
+
async function loadMiniflare() {
|
|
60
|
+
const searchRoots = [];
|
|
61
|
+
// 1. User's current project
|
|
62
|
+
searchRoots.push(process.cwd());
|
|
63
|
+
// 2. Creek-managed cache
|
|
64
|
+
searchRoots.push(join(homedir(), ".creek", "deps"));
|
|
65
|
+
// 3. Global npm root (best-effort — may be slow on first call)
|
|
66
|
+
try {
|
|
67
|
+
const globalRoot = execSync("npm root -g", {
|
|
68
|
+
encoding: "utf-8",
|
|
69
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
70
|
+
}).trim();
|
|
71
|
+
if (globalRoot)
|
|
72
|
+
searchRoots.push(globalRoot);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// No npm on PATH, or command failed — skip this search path silently.
|
|
76
|
+
}
|
|
77
|
+
for (const root of searchRoots) {
|
|
78
|
+
try {
|
|
79
|
+
// createRequire needs a file path — use a dummy file inside the root
|
|
80
|
+
// (the file doesn't have to exist for node_modules lookup to work).
|
|
81
|
+
const req = createRequire(join(root, "__creek_resolver__.js"));
|
|
82
|
+
const resolved = req.resolve("miniflare");
|
|
83
|
+
return (await import(resolved));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Try the next root.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw new MiniflareNotInstalledError();
|
|
90
|
+
}
|
|
9
91
|
const NODE_BUILTINS = [
|
|
10
92
|
"node:async_hooks",
|
|
11
93
|
"node:stream",
|
|
@@ -52,9 +134,14 @@ export class WorkerRunner {
|
|
|
52
134
|
writeFileSync(wrapperPath, wrapper);
|
|
53
135
|
// 2. Initial bundle with esbuild
|
|
54
136
|
const bundledScript = await this.bundle(wrapperPath);
|
|
55
|
-
// 3. Start Miniflare
|
|
137
|
+
// 3. Start Miniflare — loaded from the user's project / cache / global
|
|
138
|
+
// so `creek deploy` can ship without miniflare in @solcreek/cli's
|
|
139
|
+
// runtime dependencies. See loadMiniflare() above for the search
|
|
140
|
+
// order and the MiniflareNotInstalledError message.
|
|
141
|
+
const miniflareModule = await loadMiniflare();
|
|
142
|
+
const MiniflareCtor = miniflareModule.Miniflare;
|
|
56
143
|
this.mfOptions = this.buildMiniflareOptions(bundledScript, persistDir);
|
|
57
|
-
this.mf = new
|
|
144
|
+
this.mf = new MiniflareCtor(this.mfOptions);
|
|
58
145
|
// Wait for Miniflare to be ready and get the port
|
|
59
146
|
const readyUrl = await this.mf.ready;
|
|
60
147
|
// mf.ready returns a URL object
|
package/dist/utils/output.js
CHANGED
|
@@ -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 --
|
|
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/dist/utils/repo-url.d.ts
CHANGED
|
@@ -28,8 +28,11 @@ export declare function isRepoUrl(input: string): boolean;
|
|
|
28
28
|
* https://github.com/owner/repo.git
|
|
29
29
|
* https://github.com/owner/repo#branch
|
|
30
30
|
* https://github.com/owner/repo/tree/branch
|
|
31
|
-
* github:owner/repo
|
|
32
|
-
*
|
|
31
|
+
* github:owner/repo (long-form shorthand)
|
|
32
|
+
* gh:owner/repo (short alias — matches gh CLI convention)
|
|
33
|
+
* gh:owner/repo#branch
|
|
34
|
+
* gitlab:owner/repo gl:owner/repo
|
|
35
|
+
* bitbucket:owner/repo bb:owner/repo
|
|
33
36
|
*/
|
|
34
37
|
export declare function parseRepoUrl(input: string): ParsedRepoUrl;
|
|
35
38
|
/**
|
package/dist/utils/repo-url.js
CHANGED
|
@@ -17,10 +17,17 @@ const PROVIDER_HOSTS = {
|
|
|
17
17
|
"bitbucket.org": "bitbucket",
|
|
18
18
|
"www.bitbucket.org": "bitbucket",
|
|
19
19
|
};
|
|
20
|
+
// Shorthand prefixes. Short aliases (gh:/gl:/bb:) match the conventions
|
|
21
|
+
// established by the GitHub CLI, GitLab CLI, and other tooling — they're
|
|
22
|
+
// the shortest safe way to reference a repo without colliding with local
|
|
23
|
+
// path syntax (local paths never have ':' before any '/').
|
|
20
24
|
const SHORTHAND_PREFIXES = {
|
|
21
25
|
"github:": "github",
|
|
26
|
+
"gh:": "github",
|
|
22
27
|
"gitlab:": "gitlab",
|
|
28
|
+
"gl:": "gitlab",
|
|
23
29
|
"bitbucket:": "bitbucket",
|
|
30
|
+
"bb:": "bitbucket",
|
|
24
31
|
};
|
|
25
32
|
// Strict character set for owner and repo names
|
|
26
33
|
const SAFE_NAME = /^[a-zA-Z0-9._-]+$/;
|
|
@@ -53,8 +60,11 @@ export function isRepoUrl(input) {
|
|
|
53
60
|
* https://github.com/owner/repo.git
|
|
54
61
|
* https://github.com/owner/repo#branch
|
|
55
62
|
* https://github.com/owner/repo/tree/branch
|
|
56
|
-
* github:owner/repo
|
|
57
|
-
*
|
|
63
|
+
* github:owner/repo (long-form shorthand)
|
|
64
|
+
* gh:owner/repo (short alias — matches gh CLI convention)
|
|
65
|
+
* gh:owner/repo#branch
|
|
66
|
+
* gitlab:owner/repo gl:owner/repo
|
|
67
|
+
* bitbucket:owner/repo bb:owner/repo
|
|
58
68
|
*/
|
|
59
69
|
export function parseRepoUrl(input) {
|
|
60
70
|
if (!input || typeof input !== "string") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solcreek/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "CLI for the Creek deployment platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,10 +31,9 @@
|
|
|
31
31
|
"citty": "^0.1.6",
|
|
32
32
|
"consola": "^3.4.2",
|
|
33
33
|
"esbuild": "^0.25.0",
|
|
34
|
-
"miniflare": "^4.20260317.3",
|
|
35
34
|
"smol-toml": "^1.3.1",
|
|
36
35
|
"ws": "^8.20.0",
|
|
37
|
-
"@solcreek/sdk": "0.4.
|
|
36
|
+
"@solcreek/sdk": "0.4.2"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
39
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -44,6 +43,7 @@
|
|
|
44
43
|
"@types/ws": "^8.18.1",
|
|
45
44
|
"hono": "^4.7.4",
|
|
46
45
|
"jsdom": "^29.0.1",
|
|
46
|
+
"miniflare": "^4.20260317.3",
|
|
47
47
|
"react": "^19.2.4",
|
|
48
48
|
"react-dom": "^19.2.4",
|
|
49
49
|
"typescript": "^5.8.2",
|