@solcreek/cli 0.4.3 → 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 +96 -0
- package/README.md +8 -5
- package/dist/commands/deploy.d.ts +34 -0
- package/dist/commands/deploy.js +410 -9
- package/dist/commands/status.js +1 -1
- package/dist/utils/nextjs.d.ts +8 -4
- package/dist/utils/nextjs.js +11 -7
- package/dist/utils/output.js +1 -1
- package/package.json +11 -10
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @solcreek/cli
|
|
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
|
+
|
|
50
|
+
## 0.4.4
|
|
51
|
+
|
|
52
|
+
### Features
|
|
53
|
+
|
|
54
|
+
- **`creek deploy --from-github [--project <slug>]`** — trigger a deploy
|
|
55
|
+
of the latest commit on a project's production branch via its
|
|
56
|
+
stored GitHub connection, skipping the local build entirely. Same
|
|
57
|
+
server code path that a real `git push` webhook uses — runs in
|
|
58
|
+
remote-builder, deploys via the existing pipeline, posts commit
|
|
59
|
+
status. Use `--project` to target by slug or UUID, or omit it to
|
|
60
|
+
infer from `creek.toml` in the current directory.
|
|
61
|
+
|
|
62
|
+
Flow: the CLI snapshots the newest existing deployment, POSTs
|
|
63
|
+
`/github/deploy-latest`, then polls `/projects/:id/deployments`
|
|
64
|
+
every 1.5–2s to pick up the new row (handlePush runs in
|
|
65
|
+
`waitUntil` on the server, so the row appears a beat after the
|
|
66
|
+
trigger). Streams status transitions to the terminal until the
|
|
67
|
+
deployment settles on `active`, `failed`, or `cancelled`. Hard
|
|
68
|
+
cap of 15 minutes. `--json` prints a single structured result.
|
|
69
|
+
|
|
70
|
+
Pairs with the dashboard's new "Deploy latest" button on the
|
|
71
|
+
project detail page — both call the same endpoint.
|
|
72
|
+
|
|
73
|
+
## 0.4.3
|
|
74
|
+
|
|
75
|
+
- Cron/queue trigger support flowed through the deploy pipeline
|
|
76
|
+
(bump commit `c1110b1`).
|
|
77
|
+
|
|
78
|
+
## 0.4.2
|
|
79
|
+
|
|
80
|
+
- Bundled with the queue binding + worker wrapper work landed in
|
|
81
|
+
`@solcreek/runtime` 0.4.0 (bump commit `28fdb11`).
|
|
82
|
+
|
|
83
|
+
## 0.4.1
|
|
84
|
+
|
|
85
|
+
- Bump alongside `@solcreek/sdk` 0.4.0 (semantic resource names in
|
|
86
|
+
`creek.toml`).
|
|
87
|
+
|
|
88
|
+
## 0.4.0
|
|
89
|
+
|
|
90
|
+
- Version bump ahead of the cron trigger pipeline work.
|
|
91
|
+
|
|
92
|
+
## 0.3.9
|
|
93
|
+
|
|
94
|
+
- First tagged release via the publish-cli GitHub Actions workflow
|
|
95
|
+
(the workflow has been dormant since; 0.4.x went out via manual
|
|
96
|
+
`pnpm publish` until 0.4.4 restored automated releases).
|
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
|
|
|
@@ -14,6 +14,16 @@ export declare const deployCommand: import("citty").CommandDef<{
|
|
|
14
14
|
description: string;
|
|
15
15
|
required: false;
|
|
16
16
|
};
|
|
17
|
+
"from-github": {
|
|
18
|
+
type: "boolean";
|
|
19
|
+
description: string;
|
|
20
|
+
default: false;
|
|
21
|
+
};
|
|
22
|
+
project: {
|
|
23
|
+
type: "string";
|
|
24
|
+
description: string;
|
|
25
|
+
required: false;
|
|
26
|
+
};
|
|
17
27
|
json: {
|
|
18
28
|
type: "boolean";
|
|
19
29
|
description: string;
|
|
@@ -34,7 +44,31 @@ export declare const deployCommand: import("citty").CommandDef<{
|
|
|
34
44
|
description: string;
|
|
35
45
|
default: false;
|
|
36
46
|
};
|
|
47
|
+
"dry-run": {
|
|
48
|
+
type: "boolean";
|
|
49
|
+
description: string;
|
|
50
|
+
default: false;
|
|
51
|
+
};
|
|
37
52
|
}>;
|
|
53
|
+
export interface CliDeployment {
|
|
54
|
+
id: string;
|
|
55
|
+
version: number;
|
|
56
|
+
status: string;
|
|
57
|
+
branch: string | null;
|
|
58
|
+
failedStep: string | null;
|
|
59
|
+
errorMessage: string | null;
|
|
60
|
+
createdAt: number;
|
|
61
|
+
url: string | null;
|
|
62
|
+
}
|
|
63
|
+
export declare const CLI_TERMINAL_STATUSES: Set<string>;
|
|
64
|
+
export declare const CLI_IN_FLIGHT_STATUSES: Set<string>;
|
|
65
|
+
/**
|
|
66
|
+
* Given a deployment list and a "previous latest createdAt" snapshot,
|
|
67
|
+
* return the most recently-created deployment that arrived after the
|
|
68
|
+
* snapshot, or null if none have appeared yet. Used by `--from-github`
|
|
69
|
+
* to find the row that handlePush just inserted in the background.
|
|
70
|
+
*/
|
|
71
|
+
export declare function findNewDeployment(deployments: CliDeployment[], previousLatestCreatedAt: number): CliDeployment | null;
|
|
38
72
|
/**
|
|
39
73
|
* Patch bare Node.js module imports (e.g. `from "fs"`) to use the `node:` prefix
|
|
40
74
|
* (e.g. `from "node:fs"`). Workerd requires the `node:` prefix for all built-in modules.
|
package/dist/commands/deploy.js
CHANGED
|
@@ -10,7 +10,7 @@ import { collectAssets } from "../utils/bundle.js";
|
|
|
10
10
|
import { bundleSSRServer } from "../utils/ssr-bundle.js";
|
|
11
11
|
import { bundleWorker } from "../utils/worker-bundle.js";
|
|
12
12
|
import { sandboxDeploy, pollSandboxStatus, printSandboxSuccess } from "../utils/sandbox.js";
|
|
13
|
-
import { isTTY, jsonOutput, resolveJsonMode, globalArgs, shouldAutoConfirm, NO_PROJECT_BREADCRUMBS } from "../utils/output.js";
|
|
13
|
+
import { isTTY, jsonOutput, resolveJsonMode, globalArgs, shouldAutoConfirm, AUTH_BREADCRUMBS, NO_PROJECT_BREADCRUMBS } from "../utils/output.js";
|
|
14
14
|
import { ensureTosAccepted } from "../utils/tos.js";
|
|
15
15
|
import { buildNextjs, patchBundledWorker, hasAdapterOutput } from "../utils/nextjs.js";
|
|
16
16
|
import { isRepoUrl, parseRepoUrl, validateRepoUrl, validateSubpath, RepoUrlError } from "../utils/repo-url.js";
|
|
@@ -30,41 +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",
|
|
202
|
+
required: false,
|
|
203
|
+
},
|
|
204
|
+
"from-github": {
|
|
205
|
+
type: "boolean",
|
|
206
|
+
description: "Skip local build; trigger a remote deploy of the latest commit on the project's production branch via its GitHub connection.",
|
|
207
|
+
default: false,
|
|
208
|
+
},
|
|
209
|
+
project: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "Target project slug or UUID. Required with --from-github when not run inside a project directory.",
|
|
63
212
|
required: false,
|
|
64
213
|
},
|
|
65
214
|
},
|
|
66
215
|
async run({ args }) {
|
|
67
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
|
+
}
|
|
68
227
|
// --- Ensure ToS accepted ---
|
|
69
228
|
const autoConfirm = shouldAutoConfirm(args);
|
|
70
229
|
const tos = await ensureTosAccepted(autoConfirm);
|
|
@@ -82,6 +241,14 @@ export const deployCommand = defineCommand({
|
|
|
82
241
|
}
|
|
83
242
|
return await deployTemplate(args.template, templateData);
|
|
84
243
|
}
|
|
244
|
+
// --- Trigger a deploy from the project's GitHub connection (no local build) ---
|
|
245
|
+
if (args["from-github"]) {
|
|
246
|
+
return await deployFromGithub({
|
|
247
|
+
project: args.project,
|
|
248
|
+
cwd: args.dir ? resolve(args.dir) : process.cwd(),
|
|
249
|
+
jsonMode,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
85
252
|
// --- Repo URL deploy (creek deploy https://github.com/user/repo) ---
|
|
86
253
|
if (args.dir && isRepoUrl(args.dir)) {
|
|
87
254
|
return await deployRepoUrl(args.dir, {
|
|
@@ -145,6 +312,210 @@ export const deployCommand = defineCommand({
|
|
|
145
312
|
process.exit(1);
|
|
146
313
|
},
|
|
147
314
|
});
|
|
315
|
+
export const CLI_TERMINAL_STATUSES = new Set(["active", "failed", "cancelled"]);
|
|
316
|
+
export const CLI_IN_FLIGHT_STATUSES = new Set([
|
|
317
|
+
"queued",
|
|
318
|
+
"uploading",
|
|
319
|
+
"provisioning",
|
|
320
|
+
"deploying",
|
|
321
|
+
]);
|
|
322
|
+
/**
|
|
323
|
+
* Given a deployment list and a "previous latest createdAt" snapshot,
|
|
324
|
+
* return the most recently-created deployment that arrived after the
|
|
325
|
+
* snapshot, or null if none have appeared yet. Used by `--from-github`
|
|
326
|
+
* to find the row that handlePush just inserted in the background.
|
|
327
|
+
*/
|
|
328
|
+
export function findNewDeployment(deployments, previousLatestCreatedAt) {
|
|
329
|
+
const newer = deployments.filter((d) => d.createdAt > previousLatestCreatedAt);
|
|
330
|
+
if (newer.length === 0)
|
|
331
|
+
return null;
|
|
332
|
+
return newer.reduce((latest, d) => (d.createdAt > latest.createdAt ? d : latest), newer[0]);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Trigger a deploy that uses the project's github_connection instead of a
|
|
336
|
+
* local build. Polls the deployments list until the new row settles and
|
|
337
|
+
* streams status transitions to the terminal.
|
|
338
|
+
*
|
|
339
|
+
* The server endpoint (POST /github/deploy-latest) returns 200 immediately
|
|
340
|
+
* while handlePush runs in waitUntil, so we fingerprint the deployments list
|
|
341
|
+
* before the POST and look for a new row afterward.
|
|
342
|
+
*/
|
|
343
|
+
async function deployFromGithub(options) {
|
|
344
|
+
const { project: projectArg, cwd, jsonMode } = options;
|
|
345
|
+
const token = getToken();
|
|
346
|
+
if (!token) {
|
|
347
|
+
if (jsonMode)
|
|
348
|
+
jsonOutput({ error: "not_authenticated", message: "Run `creek login` first." }, 1, AUTH_BREADCRUMBS);
|
|
349
|
+
consola.error("Not logged in. Run `creek login` first.");
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
// Resolve the target project: explicit --project flag wins, otherwise
|
|
353
|
+
// fall back to the current directory's resolved config (creek.toml
|
|
354
|
+
// [project] name or inferred project name).
|
|
355
|
+
let projectSlug = projectArg;
|
|
356
|
+
if (!projectSlug) {
|
|
357
|
+
try {
|
|
358
|
+
const resolved = resolveConfig(cwd);
|
|
359
|
+
projectSlug = resolved.projectName;
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
if (!(err instanceof ConfigNotFoundError))
|
|
363
|
+
throw err;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!projectSlug) {
|
|
367
|
+
if (jsonMode)
|
|
368
|
+
jsonOutput({ error: "validation", message: "Could not determine target project. Pass --project <slug>." }, 1, NO_PROJECT_BREADCRUMBS);
|
|
369
|
+
consola.error("Could not determine target project. Pass --project <slug> or run inside a directory with a creek.toml.");
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
const client = new CreekClient(getApiUrl(), token);
|
|
373
|
+
// Snapshot the current newest deployment so we can detect the one this
|
|
374
|
+
// command creates. createdAt is the cleanest marker — the version number
|
|
375
|
+
// also works but we don't need to parse it.
|
|
376
|
+
let previousLatestCreatedAt = 0;
|
|
377
|
+
try {
|
|
378
|
+
const existing = (await client.listDeployments(projectSlug));
|
|
379
|
+
previousLatestCreatedAt = existing.reduce((max, d) => Math.max(max, d.createdAt), 0);
|
|
380
|
+
}
|
|
381
|
+
catch (err) {
|
|
382
|
+
if (err instanceof CreekAuthError)
|
|
383
|
+
throw err;
|
|
384
|
+
if (jsonMode)
|
|
385
|
+
jsonOutput({ error: "not_found", message: err.message }, 1, NO_PROJECT_BREADCRUMBS);
|
|
386
|
+
consola.error(`Could not load deployments for project '${projectSlug}': ${err.message}`);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
// Kick off the build
|
|
390
|
+
if (!jsonMode) {
|
|
391
|
+
section("Trigger");
|
|
392
|
+
consola.start(` Triggering deploy of '${projectSlug}' from its GitHub connection...`);
|
|
393
|
+
}
|
|
394
|
+
let triggerResult;
|
|
395
|
+
try {
|
|
396
|
+
triggerResult = await client.deployFromGithub(projectSlug);
|
|
397
|
+
}
|
|
398
|
+
catch (err) {
|
|
399
|
+
const msg = err.message;
|
|
400
|
+
if (jsonMode)
|
|
401
|
+
jsonOutput({ error: "trigger_failed", message: msg }, 1, NO_PROJECT_BREADCRUMBS);
|
|
402
|
+
consola.error(`Trigger failed: ${msg}`);
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
if (!jsonMode) {
|
|
406
|
+
consola.success(` Triggered (${triggerResult.branch} @ ${triggerResult.commitSha.slice(0, 7)})`);
|
|
407
|
+
section("Watch");
|
|
408
|
+
}
|
|
409
|
+
// Poll for the new deployment row, then follow its status until it settles.
|
|
410
|
+
// First phase: wait up to 20s for the row to appear (handlePush runs in
|
|
411
|
+
// waitUntil and may take a beat to insert).
|
|
412
|
+
const startedAt = Date.now();
|
|
413
|
+
const rowAppearDeadline = startedAt + 20_000;
|
|
414
|
+
const overallDeadline = startedAt + 15 * 60_000; // 15 min hard cap
|
|
415
|
+
let targetDeployment = null;
|
|
416
|
+
let lastStatus = null;
|
|
417
|
+
while (Date.now() < overallDeadline) {
|
|
418
|
+
let list;
|
|
419
|
+
try {
|
|
420
|
+
list = (await client.listDeployments(projectSlug));
|
|
421
|
+
}
|
|
422
|
+
catch (err) {
|
|
423
|
+
if (!jsonMode)
|
|
424
|
+
consola.warn(` Poll failed: ${err.message}`);
|
|
425
|
+
await sleep(2000);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
// Phase 1: find the new row. It's the newest deployment with a
|
|
429
|
+
// createdAt greater than our snapshot. triggerType should be 'github'
|
|
430
|
+
// but we don't require it (rollback/promote could race, but extremely
|
|
431
|
+
// unlikely in the narrow window between snapshot + poll).
|
|
432
|
+
if (!targetDeployment) {
|
|
433
|
+
const candidate = list
|
|
434
|
+
.filter((d) => d.createdAt > previousLatestCreatedAt)
|
|
435
|
+
.sort((a, b) => b.createdAt - a.createdAt)[0];
|
|
436
|
+
if (candidate) {
|
|
437
|
+
targetDeployment = candidate;
|
|
438
|
+
if (!jsonMode) {
|
|
439
|
+
consola.info(` v${candidate.version} ${candidate.branch ?? ""}`.trim());
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else if (Date.now() > rowAppearDeadline) {
|
|
443
|
+
// 20s and no new row — handlePush must have errored before it could
|
|
444
|
+
// insert. Surface the timeout rather than hanging.
|
|
445
|
+
if (jsonMode)
|
|
446
|
+
jsonOutput({ error: "no_deployment", message: "Deployment row did not appear within 20s. Check server logs." }, 1, NO_PROJECT_BREADCRUMBS);
|
|
447
|
+
consola.error(" Deployment row did not appear within 20s. Check control-plane logs.");
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
await sleep(1500);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// Phase 2: follow the target row's status
|
|
456
|
+
const current = list.find((d) => d.id === targetDeployment.id);
|
|
457
|
+
if (!current) {
|
|
458
|
+
// Shouldn't happen — row was there a moment ago
|
|
459
|
+
await sleep(1500);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
targetDeployment = current;
|
|
463
|
+
if (current.status !== lastStatus) {
|
|
464
|
+
lastStatus = current.status;
|
|
465
|
+
if (!jsonMode) {
|
|
466
|
+
if (CLI_IN_FLIGHT_STATUSES.has(current.status)) {
|
|
467
|
+
consola.start(` ${current.status}...`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (CLI_TERMINAL_STATUSES.has(current.status)) {
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
await sleep(2000);
|
|
475
|
+
}
|
|
476
|
+
if (!targetDeployment) {
|
|
477
|
+
if (jsonMode)
|
|
478
|
+
jsonOutput({ error: "timeout", message: "Timed out waiting for deployment" }, 1, NO_PROJECT_BREADCRUMBS);
|
|
479
|
+
consola.error("Timed out waiting for deployment");
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
// Report the outcome
|
|
483
|
+
if (targetDeployment.status === "active") {
|
|
484
|
+
if (jsonMode) {
|
|
485
|
+
jsonOutput({
|
|
486
|
+
ok: true,
|
|
487
|
+
deploymentId: targetDeployment.id,
|
|
488
|
+
version: targetDeployment.version,
|
|
489
|
+
status: "active",
|
|
490
|
+
url: targetDeployment.url,
|
|
491
|
+
}, 0, NO_PROJECT_BREADCRUMBS);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
consola.success(` Deployed v${targetDeployment.version}`);
|
|
495
|
+
if (targetDeployment.url)
|
|
496
|
+
consola.log(` ${targetDeployment.url}`);
|
|
497
|
+
}
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
// Failed / cancelled
|
|
501
|
+
const failureMsg = targetDeployment.errorMessage || `Deployment ${targetDeployment.status}`;
|
|
502
|
+
if (jsonMode) {
|
|
503
|
+
jsonOutput({
|
|
504
|
+
ok: false,
|
|
505
|
+
deploymentId: targetDeployment.id,
|
|
506
|
+
status: targetDeployment.status,
|
|
507
|
+
failedStep: targetDeployment.failedStep,
|
|
508
|
+
errorMessage: failureMsg,
|
|
509
|
+
}, 1, NO_PROJECT_BREADCRUMBS);
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
consola.error(` Deployment ${targetDeployment.status}${targetDeployment.failedStep ? ` at ${targetDeployment.failedStep}` : ""}: ${failureMsg}`);
|
|
513
|
+
}
|
|
514
|
+
process.exit(1);
|
|
515
|
+
}
|
|
516
|
+
function sleep(ms) {
|
|
517
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
518
|
+
}
|
|
148
519
|
async function deployRepoUrl(input, options) {
|
|
149
520
|
const { path: subpath, skipBuild, json: jsonMode } = options;
|
|
150
521
|
try {
|
|
@@ -268,9 +639,39 @@ async function deployDirectory(dir, jsonMode, tos) {
|
|
|
268
639
|
// Sandbox deploy — auto-detect framework, build, deploy
|
|
269
640
|
// ============================================================================
|
|
270
641
|
async function deploySandbox(cwd, skipBuild, jsonMode = false, resolved, tos) {
|
|
271
|
-
|
|
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);
|
|
272
674
|
// Detect Next.js mode (static vs opennext SSR)
|
|
273
|
-
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
274
675
|
const nextjsMode = framework === "nextjs" ? detectNextjsMode(pkg, cwd) : null;
|
|
275
676
|
const monorepo = detectMonorepo(cwd);
|
|
276
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);
|
package/dist/utils/nextjs.d.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Next.js-specific build utilities for Creek CLI.
|
|
3
3
|
*
|
|
4
4
|
* Two build paths:
|
|
5
|
-
* - **Adapter path** (Next.js >= 16.2): Uses @solcreek/adapter-
|
|
5
|
+
* - **Adapter path** (Next.js >= 16.2.3): Uses @solcreek/adapter-creek via
|
|
6
6
|
* NEXT_ADAPTER_PATH. Zero workarounds, typed outputs, direct esbuild bundle.
|
|
7
|
-
*
|
|
7
|
+
* Min version reflects the adapter's peerDependency (CVE-2026-23869).
|
|
8
|
+
* - **Legacy path** (Next.js < 16.2.3): Uses @opennextjs/cloudflare with
|
|
8
9
|
* workarounds (standalone patch, middleware manifest inline, etc.)
|
|
9
10
|
*
|
|
10
11
|
* The CLI auto-detects the Next.js version and picks the right path.
|
|
@@ -14,8 +15,11 @@ export declare function getNextVersion(cwd: string): string | null;
|
|
|
14
15
|
/**
|
|
15
16
|
* Unified Next.js build entry point.
|
|
16
17
|
*
|
|
17
|
-
* - Next.js >= 16.2: Creek adapter path (recommended)
|
|
18
|
-
* - Next.js < 16.2: legacy opennext path (best effort)
|
|
18
|
+
* - Next.js >= 16.2.3: Creek adapter path (recommended)
|
|
19
|
+
* - Next.js < 16.2.3: legacy opennext path (best effort)
|
|
20
|
+
*
|
|
21
|
+
* Min version for the adapter path matches @solcreek/adapter-creek's
|
|
22
|
+
* peerDependency, which pins Next.js >= 16.2.3 to fix CVE-2026-23869.
|
|
19
23
|
*/
|
|
20
24
|
export declare function buildNextjs(cwd: string, isMonorepo: boolean, projectName?: string): void;
|
|
21
25
|
/** Check if the adapter output exists (vs legacy opennext output). */
|
package/dist/utils/nextjs.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Next.js-specific build utilities for Creek CLI.
|
|
3
3
|
*
|
|
4
4
|
* Two build paths:
|
|
5
|
-
* - **Adapter path** (Next.js >= 16.2): Uses @solcreek/adapter-
|
|
5
|
+
* - **Adapter path** (Next.js >= 16.2.3): Uses @solcreek/adapter-creek via
|
|
6
6
|
* NEXT_ADAPTER_PATH. Zero workarounds, typed outputs, direct esbuild bundle.
|
|
7
|
-
*
|
|
7
|
+
* Min version reflects the adapter's peerDependency (CVE-2026-23869).
|
|
8
|
+
* - **Legacy path** (Next.js < 16.2.3): Uses @opennextjs/cloudflare with
|
|
8
9
|
* workarounds (standalone patch, middleware manifest inline, etc.)
|
|
9
10
|
*
|
|
10
11
|
* The CLI auto-detects the Next.js version and picks the right path.
|
|
@@ -39,7 +40,7 @@ function semverGte(version, target) {
|
|
|
39
40
|
return aPat >= bPat;
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
|
-
* Build a Next.js app using the Creek adapter (>= 16.2).
|
|
43
|
+
* Build a Next.js app using the Creek adapter (>= 16.2.3).
|
|
43
44
|
*
|
|
44
45
|
* Sets NEXT_ADAPTER_PATH to the adapter bundled with the CLI.
|
|
45
46
|
* No opennext, no wrangler, no config patching — the adapter handles
|
|
@@ -57,7 +58,7 @@ function resolveAdapterPath() {
|
|
|
57
58
|
function buildWithAdapter(cwd) {
|
|
58
59
|
const adapterPath = resolveAdapterPath();
|
|
59
60
|
if (!adapterPath) {
|
|
60
|
-
consola.warn(" @solcreek/adapter-
|
|
61
|
+
consola.warn(" @solcreek/adapter-creek not found — install it for optimal Next.js builds");
|
|
61
62
|
return; // caller falls back to legacy
|
|
62
63
|
}
|
|
63
64
|
consola.start(" Building Next.js with Creek adapter...\n");
|
|
@@ -72,12 +73,15 @@ function buildWithAdapter(cwd) {
|
|
|
72
73
|
/**
|
|
73
74
|
* Unified Next.js build entry point.
|
|
74
75
|
*
|
|
75
|
-
* - Next.js >= 16.2: Creek adapter path (recommended)
|
|
76
|
-
* - Next.js < 16.2: legacy opennext path (best effort)
|
|
76
|
+
* - Next.js >= 16.2.3: Creek adapter path (recommended)
|
|
77
|
+
* - Next.js < 16.2.3: legacy opennext path (best effort)
|
|
78
|
+
*
|
|
79
|
+
* Min version for the adapter path matches @solcreek/adapter-creek's
|
|
80
|
+
* peerDependency, which pins Next.js >= 16.2.3 to fix CVE-2026-23869.
|
|
77
81
|
*/
|
|
78
82
|
export function buildNextjs(cwd, isMonorepo, projectName) {
|
|
79
83
|
const version = getNextVersion(cwd);
|
|
80
|
-
const useAdapter = version && semverGte(version, "16.2.
|
|
84
|
+
const useAdapter = version && semverGte(version, "16.2.3") && resolveAdapterPath();
|
|
81
85
|
if (useAdapter) {
|
|
82
86
|
buildWithAdapter(cwd);
|
|
83
87
|
}
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solcreek/cli",
|
|
3
|
-
"version": "0.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",
|
|
@@ -8,14 +8,9 @@
|
|
|
8
8
|
"dist",
|
|
9
9
|
"!dist/**/*.test.*",
|
|
10
10
|
"!dist/**/*.map",
|
|
11
|
+
"CHANGELOG.md",
|
|
11
12
|
"LICENSE"
|
|
12
13
|
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "tsc",
|
|
15
|
-
"dev": "tsc --watch",
|
|
16
|
-
"typecheck": "tsc --noEmit",
|
|
17
|
-
"clean": "rm -rf dist"
|
|
18
|
-
},
|
|
19
14
|
"keywords": [
|
|
20
15
|
"creek",
|
|
21
16
|
"cli",
|
|
@@ -32,14 +27,14 @@
|
|
|
32
27
|
"directory": "packages/cli"
|
|
33
28
|
},
|
|
34
29
|
"dependencies": {
|
|
35
|
-
"@solcreek/sdk": "workspace:*",
|
|
36
30
|
"ajv": "^8.17.1",
|
|
37
31
|
"citty": "^0.1.6",
|
|
38
32
|
"consola": "^3.4.2",
|
|
39
33
|
"esbuild": "^0.25.0",
|
|
40
34
|
"miniflare": "^4.20260317.3",
|
|
41
35
|
"smol-toml": "^1.3.1",
|
|
42
|
-
"ws": "^8.20.0"
|
|
36
|
+
"ws": "^8.20.0",
|
|
37
|
+
"@solcreek/sdk": "0.4.2"
|
|
43
38
|
},
|
|
44
39
|
"devDependencies": {
|
|
45
40
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -53,5 +48,11 @@
|
|
|
53
48
|
"react-dom": "^19.2.4",
|
|
54
49
|
"typescript": "^5.8.2",
|
|
55
50
|
"vite": "^6.3.5"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc",
|
|
54
|
+
"dev": "tsc --watch",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"clean": "rm -rf dist"
|
|
56
57
|
}
|
|
57
|
-
}
|
|
58
|
+
}
|