creek 0.2.2 → 0.3.0-alpha.10

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.
Files changed (51) hide show
  1. package/dist/commands/claim.d.ts +8 -0
  2. package/dist/commands/claim.js +84 -0
  3. package/dist/commands/deploy.d.ts +23 -0
  4. package/dist/commands/deploy.js +465 -0
  5. package/dist/commands/env.d.ts +2 -0
  6. package/dist/commands/env.js +87 -0
  7. package/dist/commands/init.d.ts +8 -0
  8. package/dist/commands/init.js +60 -0
  9. package/dist/commands/login.d.ts +13 -0
  10. package/dist/commands/login.js +111 -0
  11. package/dist/commands/whoami.d.ts +2 -0
  12. package/dist/commands/whoami.js +27 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.js +25 -0
  15. package/dist/utils/auth-server.d.ts +22 -0
  16. package/dist/utils/auth-server.js +91 -0
  17. package/dist/utils/bundle.d.ts +6 -0
  18. package/dist/utils/bundle.js +24 -0
  19. package/dist/utils/config.d.ts +12 -0
  20. package/dist/utils/config.js +37 -0
  21. package/dist/utils/sandbox.d.ts +45 -0
  22. package/dist/utils/sandbox.js +57 -0
  23. package/dist/utils/ssr-bundle.d.ts +6 -0
  24. package/dist/utils/ssr-bundle.js +48 -0
  25. package/package.json +38 -16
  26. package/README.md +0 -105
  27. package/bin/creek +0 -10
  28. package/examples/hello.creek +0 -6
  29. package/examples/twitter.creek +0 -23
  30. package/examples/zeromq.creek +0 -16
  31. package/lib/aggregator.coffee +0 -27
  32. package/lib/aggregators/count.coffee +0 -15
  33. package/lib/aggregators/distinct.coffee +0 -20
  34. package/lib/aggregators/max.coffee +0 -16
  35. package/lib/aggregators/mean.coffee +0 -27
  36. package/lib/aggregators/min.coffee +0 -16
  37. package/lib/aggregators/popular.coffee +0 -27
  38. package/lib/aggregators/recent.coffee +0 -10
  39. package/lib/aggregators/sum.coffee +0 -17
  40. package/lib/bootstrap.js +0 -5
  41. package/lib/compound-aggregator.coffee +0 -33
  42. package/lib/creek.coffee +0 -13
  43. package/lib/interfaces/rest.coffee +0 -30
  44. package/lib/interfaces/websocket.coffee +0 -15
  45. package/lib/lazy-bucketed-aggregator.coffee +0 -35
  46. package/lib/parsers/chunked.coffee +0 -17
  47. package/lib/parsers/json.coffee +0 -9
  48. package/lib/parsers/words.coffee +0 -6
  49. package/lib/parsers/zeromq.coffee +0 -16
  50. package/lib/runner.coffee +0 -39
  51. package/lib/timeboxed-aggregator.coffee +0 -64
@@ -0,0 +1,8 @@
1
+ export declare const claimCommand: import("citty").CommandDef<{
2
+ sandboxId: {
3
+ type: "positional";
4
+ description: string;
5
+ required: true;
6
+ };
7
+ }>;
8
+ //# sourceMappingURL=claim.d.ts.map
@@ -0,0 +1,84 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { CreekClient } from "@solcreek/sdk";
4
+ import { getToken, getApiUrl, getSandboxApiUrl } from "../utils/config.js";
5
+ export const claimCommand = defineCommand({
6
+ meta: {
7
+ name: "claim",
8
+ description: "Claim a sandbox deployment as a permanent project",
9
+ },
10
+ args: {
11
+ sandboxId: {
12
+ type: "positional",
13
+ description: "Sandbox ID to claim (shown after sandbox deploy)",
14
+ required: true,
15
+ },
16
+ },
17
+ async run({ args }) {
18
+ const token = getToken();
19
+ if (!token) {
20
+ consola.error("You need to be logged in to claim a sandbox.");
21
+ consola.info("Run `creek login` first, then `creek claim` again.");
22
+ process.exit(1);
23
+ }
24
+ const sandboxId = args.sandboxId;
25
+ // 1. Fetch sandbox info
26
+ consola.start("Looking up sandbox...");
27
+ const sandboxApiUrl = getSandboxApiUrl();
28
+ const statusRes = await fetch(`${sandboxApiUrl}/api/sandbox/${sandboxId}/status`);
29
+ if (!statusRes.ok) {
30
+ consola.error("Sandbox not found. It may have expired.");
31
+ process.exit(1);
32
+ }
33
+ const sandbox = (await statusRes.json());
34
+ if (!sandbox.claimable) {
35
+ if (sandbox.status === "expired") {
36
+ consola.error("This sandbox has expired and can no longer be claimed.");
37
+ consola.info("Run `creek deploy` to deploy your project permanently.");
38
+ }
39
+ else {
40
+ consola.error(`Sandbox is in '${sandbox.status}' state and cannot be claimed.`);
41
+ }
42
+ process.exit(1);
43
+ }
44
+ // 2. Create permanent project
45
+ consola.start("Creating permanent project...");
46
+ const client = new CreekClient(getApiUrl(), token);
47
+ const slug = sandbox.templateId ?? sandboxId;
48
+ let project;
49
+ try {
50
+ const res = await client.createProject({
51
+ slug,
52
+ framework: sandbox.framework,
53
+ });
54
+ project = res.project;
55
+ }
56
+ catch {
57
+ // Slug might conflict, try with sandbox ID suffix
58
+ const res = await client.createProject({
59
+ slug: `${slug}-${sandboxId}`,
60
+ framework: sandbox.framework,
61
+ });
62
+ project = res.project;
63
+ }
64
+ consola.success(`Created project: ${project.slug}`);
65
+ // 3. Mark sandbox as claimed
66
+ try {
67
+ await fetch(`${sandboxApiUrl}/api/sandbox/${sandboxId}/claim`, {
68
+ method: "POST",
69
+ headers: { "Content-Type": "application/json" },
70
+ body: JSON.stringify({ projectId: project.id }),
71
+ });
72
+ }
73
+ catch {
74
+ // Best effort — claim status update is non-critical
75
+ }
76
+ consola.success("Sandbox claimed!");
77
+ consola.info("");
78
+ consola.info("Next steps:");
79
+ consola.info(` cd your-project`);
80
+ consola.info(` creek init`);
81
+ consola.info(` creek deploy # deploy permanently`);
82
+ },
83
+ });
84
+ //# sourceMappingURL=claim.js.map
@@ -0,0 +1,23 @@
1
+ export declare const deployCommand: import("citty").CommandDef<{
2
+ dir: {
3
+ type: "positional";
4
+ description: string;
5
+ required: false;
6
+ };
7
+ "skip-build": {
8
+ type: "boolean";
9
+ description: string;
10
+ default: false;
11
+ };
12
+ demo: {
13
+ type: "boolean";
14
+ description: string;
15
+ default: false;
16
+ };
17
+ template: {
18
+ type: "string";
19
+ description: string;
20
+ required: false;
21
+ };
22
+ }>;
23
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1,465 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { existsSync, readFileSync, rmSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+ import { execSync, execFileSync } from "node:child_process";
6
+ import { parseConfig, CreekClient, CreekAuthError, isSSRFramework, getSSRServerEntry, getClientAssetsDir, getDefaultBuildOutput, detectFramework, } from "@solcreek/sdk";
7
+ import { getToken, getApiUrl } from "../utils/config.js";
8
+ import { collectAssets } from "../utils/bundle.js";
9
+ import { bundleSSRServer } from "../utils/ssr-bundle.js";
10
+ import { sandboxDeploy, pollSandboxStatus, printSandboxSuccess } from "../utils/sandbox.js";
11
+ export const deployCommand = defineCommand({
12
+ meta: {
13
+ name: "deploy",
14
+ description: "Deploy the current project to Creek",
15
+ },
16
+ args: {
17
+ dir: {
18
+ type: "positional",
19
+ description: "Directory to deploy (default: current directory)",
20
+ required: false,
21
+ },
22
+ "skip-build": {
23
+ type: "boolean",
24
+ description: "Skip the build step",
25
+ default: false,
26
+ },
27
+ demo: {
28
+ type: "boolean",
29
+ description: "Deploy a sample site to see Creek in action",
30
+ default: false,
31
+ },
32
+ template: {
33
+ type: "string",
34
+ description: "Deploy a template (e.g., react-dashboard, astro-landing)",
35
+ required: false,
36
+ },
37
+ },
38
+ async run({ args }) {
39
+ // --- Demo deploy (zero-friction showcase) ---
40
+ if (args.demo) {
41
+ return await deployDemo();
42
+ }
43
+ // --- Template deploy ---
44
+ if (args.template) {
45
+ return await deployTemplate(args.template);
46
+ }
47
+ // --- Resolve target directory ---
48
+ const cwd = args.dir ? resolve(args.dir) : process.cwd();
49
+ const configPath = join(cwd, "creek.toml");
50
+ const hasConfig = existsSync(configPath);
51
+ const token = getToken();
52
+ // --- Explicit directory (creek deploy ./dist) ---
53
+ if (args.dir) {
54
+ if (!existsSync(cwd)) {
55
+ consola.error(`Directory not found: ${args.dir}`);
56
+ process.exit(1);
57
+ }
58
+ return await deployDirectory(cwd);
59
+ }
60
+ // --- Authenticated deploy (creek.toml present) ---
61
+ if (hasConfig) {
62
+ if (!token) {
63
+ consola.error("Not authenticated. Run `creek login` first.");
64
+ consola.info("Or deploy without an account: remove creek.toml and run `creek deploy` again.");
65
+ process.exit(1);
66
+ }
67
+ return await deployAuthenticated(cwd, configPath, token, args["skip-build"]);
68
+ }
69
+ // --- Sandbox deploy (has package.json → auto-detect + build) ---
70
+ if (existsSync(join(cwd, "package.json"))) {
71
+ return await deploySandbox(cwd, args["skip-build"]);
72
+ }
73
+ // --- Auto-detect build output dirs ---
74
+ for (const dir of ["dist", "build", "out", ".output/public"]) {
75
+ const fullPath = resolve(cwd, dir);
76
+ if (existsSync(fullPath)) {
77
+ consola.info(`Found build output: ${dir}/`);
78
+ return await deployDirectory(fullPath);
79
+ }
80
+ }
81
+ // --- Nothing found → guide the user ---
82
+ consola.info("No project found in this directory.\n");
83
+ consola.info(" creek deploy ./dist Deploy a build output directory");
84
+ consola.info(" creek deploy --demo Deploy a sample site (see Creek in 5 seconds)");
85
+ consola.info("");
86
+ consola.info("Or create a project first:");
87
+ consola.info(" npm create vite@latest my-app && cd my-app && npx creek deploy");
88
+ process.exit(1);
89
+ },
90
+ });
91
+ // ============================================================================
92
+ // Demo deploy — pre-built page, zero dependencies, instant
93
+ // ============================================================================
94
+ const DEMO_HTML = `<!DOCTYPE html>
95
+ <html lang="en">
96
+ <head>
97
+ <meta charset="utf-8">
98
+ <meta name="viewport" content="width=device-width, initial-scale=1">
99
+ <title>Creek Deploy Demo</title>
100
+ <style>
101
+ *{margin:0;padding:0;box-sizing:border-box}
102
+ body{font-family:system-ui,-apple-system,sans-serif;background:#0a0a0a;color:#f5f5f5;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center}
103
+ .card{max-width:480px;text-align:center;padding:3rem 2rem}
104
+ h1{font-size:2rem;font-weight:700;letter-spacing:-0.03em;margin-bottom:0.5rem}
105
+ .accent{background:linear-gradient(135deg,#38bdf8,#818cf8);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
106
+ p{color:#888;line-height:1.6;margin-top:1rem}
107
+ .meta{margin-top:2rem;font-size:0.85rem;color:#555}
108
+ .cta{display:inline-block;margin-top:1.5rem;background:linear-gradient(135deg,#2563eb,#3b82f6);color:#fff;padding:10px 24px;border-radius:8px;text-decoration:none;font-weight:600;font-size:0.9rem;transition:opacity 0.15s}
109
+ .cta:hover{opacity:0.9}
110
+ code{background:#1a1a2e;padding:2px 8px;border-radius:4px;font-size:0.85rem;color:#a5b4fc}
111
+ .pulse{width:12px;height:12px;background:#22c55e;border-radius:50%;display:inline-block;margin-right:8px;animation:pulse 2s infinite}
112
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}
113
+ </style>
114
+ </head>
115
+ <body>
116
+ <div class="card">
117
+ <h1><span class="accent">Creek</span> is live.</h1>
118
+ <p>This site was deployed to the edge in seconds — no build step, no config, no account.</p>
119
+ <p style="margin-top:1.5rem"><span class="pulse"></span>Running on Cloudflare's global network</p>
120
+ <p class="meta">Now try it with your own project:</p>
121
+ <p style="margin-top:0.5rem"><code>cd your-project && npx creek deploy</code></p>
122
+ <a href="https://creek.dev" class="cta">Learn more about Creek</a>
123
+ </div>
124
+ <script>document.title="Creek Deploy Demo — "+new Date().toLocaleTimeString()</script>
125
+ </body>
126
+ </html>`;
127
+ async function deployDemo() {
128
+ consola.start("Deploying demo site...");
129
+ try {
130
+ const result = await sandboxDeploy({
131
+ assets: { "index.html": Buffer.from(DEMO_HTML).toString("base64") },
132
+ source: "cli-demo",
133
+ });
134
+ consola.start("Waiting for deployment...");
135
+ const status = await pollSandboxStatus(result.statusUrl);
136
+ const duration = status.deployDurationMs
137
+ ? `in ${(status.deployDurationMs / 1000).toFixed(1)}s`
138
+ : "in seconds";
139
+ consola.success(`Live ${duration} → ${status.previewUrl}`);
140
+ consola.info("");
141
+ consola.info("That's Creek. Now deploy your own project:");
142
+ consola.info(" cd your-project && npx creek deploy");
143
+ }
144
+ catch (err) {
145
+ consola.error(err instanceof Error ? err.message : "Demo deploy failed");
146
+ process.exit(1);
147
+ }
148
+ }
149
+ // ============================================================================
150
+ // Directory deploy — deploy pre-built static files directly
151
+ // ============================================================================
152
+ async function deployDirectory(dir) {
153
+ consola.info("Deploying to sandbox (60 min preview).");
154
+ consola.start("Collecting assets...");
155
+ const { assets, fileList } = collectAssets(dir);
156
+ if (fileList.length === 0) {
157
+ consola.error("No files found in directory.");
158
+ process.exit(1);
159
+ }
160
+ consola.info(`Found ${fileList.length} files`);
161
+ consola.start("Deploying...");
162
+ try {
163
+ const result = await sandboxDeploy({ assets, source: "cli" });
164
+ consola.start("Waiting for deployment...");
165
+ const status = await pollSandboxStatus(result.statusUrl);
166
+ printSandboxSuccess(status.previewUrl, result.expiresAt, result.sandboxId);
167
+ }
168
+ catch (err) {
169
+ consola.error(err instanceof Error ? err.message : "Deploy failed");
170
+ process.exit(1);
171
+ }
172
+ }
173
+ // ============================================================================
174
+ // Sandbox deploy — auto-detect framework, build, deploy
175
+ // ============================================================================
176
+ async function deploySandbox(cwd, skipBuild) {
177
+ consola.info("Deploying to sandbox (60 min preview).");
178
+ consola.info("");
179
+ const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
180
+ const framework = detectFramework(pkg);
181
+ if (framework) {
182
+ consola.info(`Detected: ${framework}`);
183
+ }
184
+ else {
185
+ consola.info("Framework: auto (static site)");
186
+ }
187
+ // Build
188
+ const buildCommand = pkg.scripts?.build ? "npm run build" : null;
189
+ const outputDir = resolve(cwd, getDefaultBuildOutput(framework));
190
+ if (!skipBuild) {
191
+ if (!buildCommand) {
192
+ consola.error("No build script found in package.json.");
193
+ consola.info("Add a 'build' script or use --skip-build if already built.");
194
+ process.exit(1);
195
+ }
196
+ consola.start(`Building with: ${buildCommand}`);
197
+ try {
198
+ execSync(buildCommand, { cwd, stdio: "inherit" });
199
+ }
200
+ catch {
201
+ consola.error("Build failed");
202
+ consola.info("");
203
+ consola.info("Common fixes:");
204
+ consola.info(" • npm install (missing dependencies?)");
205
+ consola.info(" • Check for TypeScript errors");
206
+ consola.info(` • Verify build works: ${buildCommand}`);
207
+ process.exit(1);
208
+ }
209
+ consola.success("Build complete");
210
+ }
211
+ if (!existsSync(outputDir)) {
212
+ consola.error(`Build output not found: ${outputDir}`);
213
+ if (framework) {
214
+ consola.info(`Expected output for ${framework}: ${getDefaultBuildOutput(framework)}`);
215
+ }
216
+ process.exit(1);
217
+ }
218
+ // Collect assets
219
+ const isSSR = isSSRFramework(framework);
220
+ const renderMode = isSSR ? "ssr" : "spa";
221
+ let clientAssetsDir = outputDir;
222
+ if (isSSR && framework) {
223
+ const subdir = getClientAssetsDir(framework);
224
+ if (subdir)
225
+ clientAssetsDir = resolve(outputDir, subdir);
226
+ }
227
+ consola.start("Collecting assets...");
228
+ const { assets: clientAssets, fileList } = collectAssets(clientAssetsDir);
229
+ consola.info(`Found ${fileList.length} assets`);
230
+ let serverFiles;
231
+ if (isSSR && framework) {
232
+ const serverEntry = getSSRServerEntry(framework);
233
+ if (serverEntry) {
234
+ const serverEntryPath = resolve(outputDir, serverEntry);
235
+ if (existsSync(serverEntryPath)) {
236
+ consola.start("Bundling SSR server...");
237
+ const bundled = await bundleSSRServer(serverEntryPath);
238
+ serverFiles = { "server.js": Buffer.from(bundled).toString("base64") };
239
+ consola.success(`SSR bundled (${Math.round(bundled.length / 1024)}KB)`);
240
+ }
241
+ }
242
+ }
243
+ // Deploy to sandbox
244
+ consola.start("Deploying...");
245
+ try {
246
+ const result = await sandboxDeploy({
247
+ assets: clientAssets,
248
+ serverFiles,
249
+ framework: framework ?? undefined,
250
+ source: "cli",
251
+ });
252
+ consola.start("Waiting for deployment...");
253
+ const status = await pollSandboxStatus(result.statusUrl);
254
+ printSandboxSuccess(status.previewUrl, result.expiresAt, result.sandboxId);
255
+ }
256
+ catch (err) {
257
+ consola.error(err instanceof Error ? err.message : "Sandbox deploy failed");
258
+ process.exit(1);
259
+ }
260
+ }
261
+ // ============================================================================
262
+ // Template deploy — clone + build + deploy to sandbox
263
+ // ============================================================================
264
+ async function deployTemplate(templateId) {
265
+ // Validate template ID — alphanumeric, hyphens, underscores only (no path traversal)
266
+ if (!/^[a-zA-Z0-9_-]+$/.test(templateId)) {
267
+ consola.error("Invalid template name. Use only letters, numbers, hyphens, and underscores.");
268
+ process.exit(1);
269
+ }
270
+ consola.info(`Deploying template: ${templateId}`);
271
+ // Clone template to temp dir
272
+ const tmpDir = join(process.env.TMPDIR ?? "/tmp", `creek-template-${Date.now()}`);
273
+ const repoUrl = "https://github.com/solcreek/templates";
274
+ consola.start("Cloning template...");
275
+ try {
276
+ execFileSync("git", [
277
+ "clone", "--depth", "1", "--filter=blob:none", "--sparse", repoUrl, tmpDir,
278
+ ], { stdio: "pipe" });
279
+ execFileSync("git", [
280
+ "sparse-checkout", "set", templateId,
281
+ ], { cwd: tmpDir, stdio: "pipe" });
282
+ }
283
+ catch {
284
+ consola.error(`Template '${templateId}' not found.`);
285
+ consola.info("Available templates: creek deploy --template");
286
+ cleanupDir(tmpDir);
287
+ process.exit(1);
288
+ }
289
+ const templateDir = join(tmpDir, templateId);
290
+ // Verify resolved path is still within tmpDir (prevent path traversal)
291
+ if (!resolve(templateDir).startsWith(resolve(tmpDir))) {
292
+ consola.error("Invalid template path.");
293
+ cleanupDir(tmpDir);
294
+ process.exit(1);
295
+ }
296
+ if (!existsSync(templateDir)) {
297
+ consola.error(`Template directory not found: ${templateId}`);
298
+ cleanupDir(tmpDir);
299
+ process.exit(1);
300
+ }
301
+ consola.start("Installing dependencies...");
302
+ try {
303
+ execFileSync("npm", ["install"], { cwd: templateDir, stdio: "pipe" });
304
+ }
305
+ catch {
306
+ consola.error("Failed to install dependencies");
307
+ cleanupDir(tmpDir);
308
+ process.exit(1);
309
+ }
310
+ // Deploy as sandbox (reuse sandbox flow)
311
+ await deploySandbox(templateDir, false);
312
+ // Cleanup
313
+ cleanupDir(tmpDir);
314
+ }
315
+ function cleanupDir(dir) {
316
+ try {
317
+ rmSync(dir, { recursive: true, force: true });
318
+ }
319
+ catch {
320
+ // ignore
321
+ }
322
+ }
323
+ // ============================================================================
324
+ // Authenticated deploy — existing flow
325
+ // ============================================================================
326
+ async function deployAuthenticated(cwd, configPath, token, skipBuild) {
327
+ try {
328
+ const config = parseConfig(readFileSync(configPath, "utf-8"));
329
+ consola.info(`Project: ${config.project.name}`);
330
+ const client = new CreekClient(getApiUrl(), token);
331
+ // Ensure project exists
332
+ let project;
333
+ try {
334
+ project = await client.getProject(config.project.name);
335
+ }
336
+ catch {
337
+ consola.info("Project not found, creating...");
338
+ const res = await client.createProject({
339
+ slug: config.project.name,
340
+ framework: config.project.framework,
341
+ });
342
+ project = res.project;
343
+ consola.success(`Created project: ${project.slug}`);
344
+ }
345
+ // Build — creek.toml build.command is user-defined, analogous to npm scripts
346
+ if (!skipBuild) {
347
+ const buildCmd = config.build.command;
348
+ if (!buildCmd || typeof buildCmd !== "string" || buildCmd.length > 500) {
349
+ consola.error("Invalid build command in creek.toml");
350
+ process.exit(1);
351
+ }
352
+ consola.start(`Building with: ${buildCmd}`);
353
+ try {
354
+ execSync(buildCmd, { cwd, stdio: "inherit" });
355
+ }
356
+ catch {
357
+ consola.error("Build failed");
358
+ process.exit(1);
359
+ }
360
+ consola.success("Build complete");
361
+ }
362
+ const outputDir = resolve(cwd, config.build.output);
363
+ if (!existsSync(outputDir)) {
364
+ consola.error(`Build output directory not found: ${config.build.output}`);
365
+ process.exit(1);
366
+ }
367
+ const framework = config.project.framework ?? null;
368
+ const isSSR = isSSRFramework(framework);
369
+ const renderMode = isSSR ? "ssr" : "spa";
370
+ let clientAssetsDir = outputDir;
371
+ if (isSSR && framework) {
372
+ const clientSubdir = getClientAssetsDir(framework);
373
+ if (clientSubdir) {
374
+ clientAssetsDir = resolve(outputDir, clientSubdir);
375
+ }
376
+ }
377
+ consola.start("Collecting assets...");
378
+ const { assets: clientAssets, fileList } = collectAssets(clientAssetsDir);
379
+ consola.info(`Found ${fileList.length} client assets`);
380
+ let serverFiles;
381
+ if (isSSR && framework) {
382
+ const serverEntry = getSSRServerEntry(framework);
383
+ if (serverEntry) {
384
+ const serverEntryPath = resolve(outputDir, serverEntry);
385
+ if (existsSync(serverEntryPath)) {
386
+ consola.start("Bundling SSR server...");
387
+ const bundled = await bundleSSRServer(serverEntryPath);
388
+ serverFiles = {
389
+ "server.js": Buffer.from(bundled).toString("base64"),
390
+ };
391
+ consola.success(`SSR server bundled (${Math.round(bundled.length / 1024)}KB)`);
392
+ }
393
+ }
394
+ }
395
+ consola.start("Creating deployment...");
396
+ const { deployment } = await client.createDeployment(project.id);
397
+ consola.start("Uploading...");
398
+ const bundle = {
399
+ manifest: {
400
+ assets: fileList,
401
+ hasWorker: isSSR,
402
+ entrypoint: null,
403
+ renderMode,
404
+ },
405
+ workerScript: null,
406
+ assets: clientAssets,
407
+ serverFiles,
408
+ resources: config.resources,
409
+ };
410
+ await client.uploadDeploymentBundle(project.id, deployment.id, bundle);
411
+ // Poll for async deploy progress
412
+ const POLL_INTERVAL = 1000;
413
+ const POLL_TIMEOUT = 120_000;
414
+ const TERMINAL = new Set(["active", "failed", "cancelled"]);
415
+ const STEP_LABELS = {
416
+ queued: "Waiting...",
417
+ uploading: "Uploading bundle...",
418
+ provisioning: "Provisioning resources...",
419
+ deploying: "Deploying to edge...",
420
+ };
421
+ let lastStatus = "";
422
+ const start = Date.now();
423
+ while (Date.now() - start < POLL_TIMEOUT) {
424
+ const res = await client.getDeploymentStatus(project.id, deployment.id);
425
+ const { status, failed_step, error_message } = res.deployment;
426
+ if (status !== lastStatus) {
427
+ if (lastStatus && STEP_LABELS[lastStatus]) {
428
+ consola.success(STEP_LABELS[lastStatus].replace("...", ""));
429
+ }
430
+ if (!TERMINAL.has(status) && STEP_LABELS[status]) {
431
+ consola.start(STEP_LABELS[status]);
432
+ }
433
+ lastStatus = status;
434
+ }
435
+ if (status === "active") {
436
+ consola.success(`Deployed! ${res.url ?? res.previewUrl}`);
437
+ if (res.url && res.previewUrl) {
438
+ consola.info(`Preview: ${res.previewUrl}`);
439
+ }
440
+ return;
441
+ }
442
+ if (status === "failed") {
443
+ const step = failed_step ? ` at ${failed_step}` : "";
444
+ const msg = error_message ?? "Unknown error";
445
+ consola.error(`Deploy failed${step}: ${msg}`);
446
+ process.exit(1);
447
+ }
448
+ if (status === "cancelled") {
449
+ consola.warn("Deploy was cancelled");
450
+ process.exit(1);
451
+ }
452
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL));
453
+ }
454
+ consola.error("Deploy timed out after 2 minutes");
455
+ process.exit(1);
456
+ }
457
+ catch (err) {
458
+ if (err instanceof CreekAuthError) {
459
+ consola.error("Authentication failed. Run `creek login` to re-authenticate.");
460
+ process.exit(1);
461
+ }
462
+ throw err;
463
+ }
464
+ }
465
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1,2 @@
1
+ export declare const envCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { CreekClient } from "@solcreek/sdk";
4
+ import { getToken, getApiUrl } from "../utils/config.js";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { parseConfig } from "@solcreek/sdk";
8
+ function getProjectSlug() {
9
+ const configPath = join(process.cwd(), "creek.toml");
10
+ if (!existsSync(configPath)) {
11
+ consola.error("No creek.toml found. Run `creek init` first.");
12
+ process.exit(1);
13
+ }
14
+ return parseConfig(readFileSync(configPath, "utf-8")).project.name;
15
+ }
16
+ function getClient() {
17
+ const token = getToken();
18
+ if (!token) {
19
+ consola.error("Not authenticated. Run `creek login` first.");
20
+ process.exit(1);
21
+ }
22
+ return new CreekClient(getApiUrl(), token);
23
+ }
24
+ const envSet = defineCommand({
25
+ meta: { name: "set", description: "Set an environment variable" },
26
+ args: {
27
+ key: { type: "positional", description: "Variable name (e.g. DATABASE_URL)", required: true },
28
+ value: { type: "positional", description: "Variable value", required: true },
29
+ },
30
+ async run({ args }) {
31
+ const client = getClient();
32
+ const slug = getProjectSlug();
33
+ await client.setEnvVar(slug, args.key, args.value);
34
+ consola.success(`Set ${args.key}`);
35
+ },
36
+ });
37
+ function redact(value) {
38
+ if (value.length <= 4)
39
+ return "••••";
40
+ return value.slice(0, 2) + "•".repeat(Math.min(value.length - 4, 20)) + value.slice(-2);
41
+ }
42
+ const envGet = defineCommand({
43
+ meta: { name: "ls", description: "List environment variables" },
44
+ args: {
45
+ show: { type: "boolean", description: "Show values in plaintext (default: redacted)", default: false },
46
+ },
47
+ async run({ args }) {
48
+ const client = getClient();
49
+ const slug = getProjectSlug();
50
+ const vars = await client.listEnvVars(slug);
51
+ if (vars.length === 0) {
52
+ consola.info("No environment variables set.");
53
+ return;
54
+ }
55
+ for (const v of vars) {
56
+ const displayed = args.show ? v.value : redact(v.value);
57
+ consola.log(` ${v.key} = ${displayed}`);
58
+ }
59
+ if (!args.show) {
60
+ consola.info(" (use --show to reveal values)");
61
+ }
62
+ },
63
+ });
64
+ const envRm = defineCommand({
65
+ meta: { name: "rm", description: "Remove an environment variable" },
66
+ args: {
67
+ key: { type: "positional", description: "Variable name to remove", required: true },
68
+ },
69
+ async run({ args }) {
70
+ const client = getClient();
71
+ const slug = getProjectSlug();
72
+ await client.deleteEnvVar(slug, args.key);
73
+ consola.success(`Removed ${args.key}`);
74
+ },
75
+ });
76
+ export const envCommand = defineCommand({
77
+ meta: {
78
+ name: "env",
79
+ description: "Manage environment variables",
80
+ },
81
+ subCommands: {
82
+ set: envSet,
83
+ ls: envGet,
84
+ rm: envRm,
85
+ },
86
+ });
87
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1,8 @@
1
+ export declare const initCommand: import("citty").CommandDef<{
2
+ name: {
3
+ type: "string";
4
+ description: string;
5
+ required: false;
6
+ };
7
+ }>;
8
+ //# sourceMappingURL=init.d.ts.map