creek 0.3.0 → 0.3.2

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 (49) hide show
  1. package/bin.js +2 -0
  2. package/hono.d.ts +1 -0
  3. package/hono.js +1 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +1 -0
  6. package/package.json +30 -35
  7. package/react.d.ts +1 -0
  8. package/react.js +1 -0
  9. package/README.md +0 -184
  10. package/dist/commands/claim.d.ts +0 -18
  11. package/dist/commands/claim.js +0 -93
  12. package/dist/commands/deploy.d.ts +0 -38
  13. package/dist/commands/deploy.js +0 -807
  14. package/dist/commands/deployments.d.ts +0 -18
  15. package/dist/commands/deployments.js +0 -84
  16. package/dist/commands/env.d.ts +0 -2
  17. package/dist/commands/env.js +0 -104
  18. package/dist/commands/init.d.ts +0 -18
  19. package/dist/commands/init.js +0 -69
  20. package/dist/commands/login.d.ts +0 -23
  21. package/dist/commands/login.js +0 -120
  22. package/dist/commands/projects.d.ts +0 -13
  23. package/dist/commands/projects.js +0 -38
  24. package/dist/commands/status.d.ts +0 -18
  25. package/dist/commands/status.js +0 -115
  26. package/dist/commands/whoami.d.ts +0 -13
  27. package/dist/commands/whoami.js +0 -43
  28. package/dist/index.d.ts +0 -3
  29. package/dist/index.js +0 -36
  30. package/dist/utils/auth-server.d.ts +0 -22
  31. package/dist/utils/auth-server.js +0 -91
  32. package/dist/utils/bundle.d.ts +0 -6
  33. package/dist/utils/bundle.js +0 -39
  34. package/dist/utils/config.d.ts +0 -12
  35. package/dist/utils/config.js +0 -37
  36. package/dist/utils/git-clone.d.ts +0 -44
  37. package/dist/utils/git-clone.js +0 -193
  38. package/dist/utils/nextjs.d.ts +0 -48
  39. package/dist/utils/nextjs.js +0 -368
  40. package/dist/utils/output.d.ts +0 -38
  41. package/dist/utils/output.js +0 -45
  42. package/dist/utils/repo-url.d.ts +0 -48
  43. package/dist/utils/repo-url.js +0 -201
  44. package/dist/utils/sandbox.d.ts +0 -50
  45. package/dist/utils/sandbox.js +0 -69
  46. package/dist/utils/ssr-bundle.d.ts +0 -6
  47. package/dist/utils/ssr-bundle.js +0 -48
  48. package/dist/utils/tos.d.ts +0 -28
  49. package/dist/utils/tos.js +0 -95
@@ -1,201 +0,0 @@
1
- /**
2
- * Git repository URL parsing and security validation.
3
- *
4
- * Security model:
5
- * - HTTPS only (no git://, ssh://, file://, ext::)
6
- * - Hostname allowlist (github.com, gitlab.com, bitbucket.org only)
7
- * - Owner/repo validated against strict character set
8
- * - No embedded credentials
9
- * - Subpath validated against traversal attacks
10
- */
11
- // --- Allowed hostnames (SSRF prevention) ---
12
- const PROVIDER_HOSTS = {
13
- "github.com": "github",
14
- "www.github.com": "github",
15
- "gitlab.com": "gitlab",
16
- "www.gitlab.com": "gitlab",
17
- "bitbucket.org": "bitbucket",
18
- "www.bitbucket.org": "bitbucket",
19
- };
20
- const SHORTHAND_PREFIXES = {
21
- "github:": "github",
22
- "gitlab:": "gitlab",
23
- "bitbucket:": "bitbucket",
24
- };
25
- // Strict character set for owner and repo names
26
- const SAFE_NAME = /^[a-zA-Z0-9._-]+$/;
27
- // --- Public API ---
28
- /**
29
- * Quick check: does this string look like a repo URL or shorthand?
30
- * Used to route between directory deploy and repo deploy.
31
- */
32
- export function isRepoUrl(input) {
33
- if (!input)
34
- return false;
35
- // Shorthand: github:user/repo
36
- for (const prefix of Object.keys(SHORTHAND_PREFIXES)) {
37
- if (input.startsWith(prefix))
38
- return true;
39
- }
40
- // HTTPS URL to known host
41
- try {
42
- const url = new URL(input.split("#")[0]);
43
- return url.protocol === "https:" && url.hostname in PROVIDER_HOSTS;
44
- }
45
- catch {
46
- return false;
47
- }
48
- }
49
- /**
50
- * Parse a repo URL or shorthand into structured components.
51
- * Supports:
52
- * https://github.com/owner/repo
53
- * https://github.com/owner/repo.git
54
- * https://github.com/owner/repo#branch
55
- * https://github.com/owner/repo/tree/branch
56
- * github:owner/repo
57
- * github:owner/repo#branch
58
- */
59
- export function parseRepoUrl(input) {
60
- if (!input || typeof input !== "string") {
61
- throw new RepoUrlError("Empty or invalid input");
62
- }
63
- // Shorthand: github:owner/repo#branch
64
- for (const [prefix, provider] of Object.entries(SHORTHAND_PREFIXES)) {
65
- if (input.startsWith(prefix)) {
66
- return parseShorthand(input.slice(prefix.length), provider);
67
- }
68
- }
69
- // Full URL
70
- return parseFullUrl(input);
71
- }
72
- /**
73
- * Validate a parsed repo URL for security.
74
- * Throws RepoUrlError on any violation.
75
- */
76
- export function validateRepoUrl(parsed) {
77
- // Protocol: only HTTPS (enforced during parsing, but belt-and-suspenders)
78
- if (!parsed.cloneUrl.startsWith("https://")) {
79
- throw new RepoUrlError("Only HTTPS URLs are allowed");
80
- }
81
- // Owner and repo name: strict character set
82
- if (!SAFE_NAME.test(parsed.owner)) {
83
- throw new RepoUrlError(`Invalid owner name: ${JSON.stringify(parsed.owner)}`);
84
- }
85
- if (!SAFE_NAME.test(parsed.repo)) {
86
- throw new RepoUrlError(`Invalid repo name: ${JSON.stringify(parsed.repo)}`);
87
- }
88
- // Branch: strict character set (if present)
89
- if (parsed.branch !== null && !/^[a-zA-Z0-9._\/-]+$/.test(parsed.branch)) {
90
- throw new RepoUrlError(`Invalid branch name: ${JSON.stringify(parsed.branch)}`);
91
- }
92
- // No null bytes anywhere
93
- const allParts = [parsed.owner, parsed.repo, parsed.branch, parsed.cloneUrl].filter(Boolean);
94
- for (const part of allParts) {
95
- if (part.includes("\0")) {
96
- throw new RepoUrlError("Null bytes are not allowed");
97
- }
98
- }
99
- }
100
- /**
101
- * Validate a --path subdirectory argument.
102
- * Prevents path traversal and other filesystem attacks.
103
- */
104
- export function validateSubpath(path) {
105
- if (!path || !path.trim()) {
106
- throw new RepoUrlError("Subpath cannot be empty");
107
- }
108
- // No absolute paths
109
- if (path.startsWith("/") || path.startsWith("\\")) {
110
- throw new RepoUrlError("Subpath must be relative, not absolute");
111
- }
112
- // No path traversal
113
- const segments = path.split(/[/\\]/);
114
- if (segments.some((s) => s === "..")) {
115
- throw new RepoUrlError("Path traversal (..) is not allowed in subpath");
116
- }
117
- // No null bytes
118
- if (path.includes("\0")) {
119
- throw new RepoUrlError("Null bytes are not allowed in subpath");
120
- }
121
- // Only safe characters
122
- if (!/^[a-zA-Z0-9._\/-]+$/.test(path)) {
123
- throw new RepoUrlError("Subpath contains invalid characters");
124
- }
125
- }
126
- // --- Internal parsers ---
127
- function parseShorthand(rest, provider) {
128
- // Split on # for branch
129
- const [pathPart, ...branchParts] = rest.split("#");
130
- const branch = branchParts.length > 0 ? branchParts.join("#") : null;
131
- const segments = pathPart.replace(/\.git$/, "").split("/").filter(Boolean);
132
- if (segments.length !== 2) {
133
- throw new RepoUrlError(`Expected owner/repo format, got: ${pathPart}`);
134
- }
135
- const [owner, repo] = segments;
136
- const host = provider === "github" ? "github.com" : provider === "gitlab" ? "gitlab.com" : "bitbucket.org";
137
- return {
138
- provider,
139
- owner,
140
- repo,
141
- branch: branch || null,
142
- cloneUrl: `https://${host}/${owner}/${repo}.git`,
143
- displayUrl: `${owner}/${repo}${branch ? `#${branch}` : ""}`,
144
- };
145
- }
146
- function parseFullUrl(input) {
147
- // Extract branch from fragment before parsing URL
148
- const [urlPart, ...fragmentParts] = input.split("#");
149
- const fragment = fragmentParts.length > 0 ? fragmentParts.join("#") : null;
150
- let url;
151
- try {
152
- // Strip query params and trailing slash
153
- const cleanUrl = urlPart.split("?")[0].replace(/\/$/, "");
154
- url = new URL(cleanUrl);
155
- }
156
- catch {
157
- throw new RepoUrlError(`Invalid URL: ${input}`);
158
- }
159
- // Protocol check
160
- if (url.protocol !== "https:") {
161
- throw new RepoUrlError(`Only HTTPS URLs are allowed. Got: ${url.protocol}`);
162
- }
163
- // Hostname allowlist (SSRF prevention)
164
- const hostname = url.hostname.toLowerCase();
165
- const provider = PROVIDER_HOSTS[hostname];
166
- if (!provider) {
167
- throw new RepoUrlError(`Unsupported host: ${hostname}. Supported: github.com, gitlab.com, bitbucket.org`);
168
- }
169
- // No embedded credentials
170
- if (url.username || url.password) {
171
- throw new RepoUrlError("URLs with embedded credentials are not allowed");
172
- }
173
- // Parse path segments: /owner/repo[/tree/branch][.git]
174
- const pathSegments = url.pathname.split("/").filter(Boolean);
175
- if (pathSegments.length < 2) {
176
- throw new RepoUrlError(`Expected /{owner}/{repo} in URL path, got: ${url.pathname}`);
177
- }
178
- const owner = pathSegments[0];
179
- const repo = pathSegments[1].replace(/\.git$/, "");
180
- // Extract branch from /tree/branch path (GitHub convention)
181
- let branch = fragment;
182
- if (pathSegments.length >= 4 && pathSegments[2] === "tree") {
183
- branch = pathSegments.slice(3).join("/");
184
- }
185
- return {
186
- provider,
187
- owner,
188
- repo,
189
- branch: branch || null,
190
- cloneUrl: `https://${hostname}/${owner}/${repo}.git`,
191
- displayUrl: `${owner}/${repo}${branch ? `#${branch}` : ""}`,
192
- };
193
- }
194
- // --- Error ---
195
- export class RepoUrlError extends Error {
196
- constructor(message) {
197
- super(message);
198
- this.name = "RepoUrlError";
199
- }
200
- }
201
- //# sourceMappingURL=repo-url.js.map
@@ -1,50 +0,0 @@
1
- import type { TosAcceptance } from "./tos.js";
2
- interface SandboxDeployResponse {
3
- sandboxId: string;
4
- status: string;
5
- statusUrl: string;
6
- previewUrl: string;
7
- expiresAt: string;
8
- tier?: string;
9
- }
10
- interface SandboxStatusResponse {
11
- sandboxId: string;
12
- status: string;
13
- previewUrl: string;
14
- deployDurationMs?: number;
15
- expiresAt: string;
16
- expiresInSeconds: number;
17
- claimable: boolean;
18
- failedStep?: string;
19
- errorMessage?: string;
20
- }
21
- /**
22
- * Deploy a bundle to the sandbox API (no auth required).
23
- * Manifest is optional — the API derives asset list from the assets keys.
24
- */
25
- export declare function sandboxDeploy(bundle: {
26
- manifest?: {
27
- assets: string[];
28
- hasWorker: boolean;
29
- entrypoint: string | null;
30
- renderMode: string;
31
- };
32
- assets: Record<string, string>;
33
- serverFiles?: Record<string, string>;
34
- framework?: string;
35
- templateId?: string;
36
- source: string;
37
- }, opts?: {
38
- tos?: TosAcceptance;
39
- agentToken?: string;
40
- }): Promise<SandboxDeployResponse>;
41
- /**
42
- * Poll sandbox status until terminal state.
43
- */
44
- export declare function pollSandboxStatus(statusUrl: string): Promise<SandboxStatusResponse>;
45
- /**
46
- * Print sandbox success message with claim instructions.
47
- */
48
- export declare function printSandboxSuccess(previewUrl: string, expiresAt: string, sandboxId: string): void;
49
- export {};
50
- //# sourceMappingURL=sandbox.d.ts.map
@@ -1,69 +0,0 @@
1
- import consola from "consola";
2
- import { getSandboxApiUrl } from "./config.js";
3
- import { isTTY } from "./output.js";
4
- /**
5
- * Deploy a bundle to the sandbox API (no auth required).
6
- * Manifest is optional — the API derives asset list from the assets keys.
7
- */
8
- export async function sandboxDeploy(bundle, opts) {
9
- const apiUrl = getSandboxApiUrl();
10
- const headers = {
11
- "Content-Type": "application/json",
12
- };
13
- // Signal TTY status for tiered rate limiting
14
- if (isTTY)
15
- headers["X-Creek-TTY"] = "1";
16
- // Agent token for elevated rate limit
17
- if (opts?.agentToken)
18
- headers["Authorization"] = `Bearer ${opts.agentToken}`;
19
- // ToS acceptance metadata
20
- if (opts?.tos) {
21
- headers["X-Creek-ToS-Version"] = opts.tos.version;
22
- headers["X-Creek-ToS-Accepted-At"] = opts.tos.acceptedAt;
23
- }
24
- const res = await fetch(`${apiUrl}/api/sandbox/deploy`, {
25
- method: "POST",
26
- headers,
27
- body: JSON.stringify(bundle),
28
- });
29
- if (!res.ok) {
30
- const err = await res.json().catch(() => ({ message: res.statusText }));
31
- throw new Error(err.message ?? `Sandbox deploy failed (${res.status})`);
32
- }
33
- return res.json();
34
- }
35
- /**
36
- * Poll sandbox status until terminal state.
37
- */
38
- export async function pollSandboxStatus(statusUrl) {
39
- const POLL_INTERVAL = 1000;
40
- const POLL_TIMEOUT = 60_000;
41
- const start = Date.now();
42
- while (Date.now() - start < POLL_TIMEOUT) {
43
- const res = await fetch(statusUrl);
44
- if (!res.ok)
45
- throw new Error(`Status check failed (${res.status})`);
46
- const status = (await res.json());
47
- if (status.status === "active")
48
- return status;
49
- if (status.status === "failed") {
50
- const step = status.failedStep ? ` at ${status.failedStep}` : "";
51
- throw new Error(`Sandbox deploy failed${step}: ${status.errorMessage ?? "Unknown error"}`);
52
- }
53
- if (status.status === "expired") {
54
- throw new Error("Sandbox expired before activation");
55
- }
56
- await new Promise((r) => setTimeout(r, POLL_INTERVAL));
57
- }
58
- throw new Error("Sandbox deploy timed out");
59
- }
60
- /**
61
- * Print sandbox success message with claim instructions.
62
- */
63
- export function printSandboxSuccess(previewUrl, expiresAt, sandboxId) {
64
- consola.success(` Live → ${previewUrl}`);
65
- consola.info("");
66
- consola.info(" Free preview — available for 60 minutes.");
67
- consola.info(" Make it permanent: creek login && creek claim " + sandboxId);
68
- }
69
- //# sourceMappingURL=sandbox.js.map
@@ -1,6 +0,0 @@
1
- /**
2
- * Bundle an SSR server entry point into a single standalone worker script.
3
- * Uses esbuild (same bundler as wrangler) with node: builtins as externals.
4
- */
5
- export declare function bundleSSRServer(entryPoint: string): Promise<string>;
6
- //# sourceMappingURL=ssr-bundle.d.ts.map
@@ -1,48 +0,0 @@
1
- import { build } from "esbuild";
2
- /**
3
- * Bundle an SSR server entry point into a single standalone worker script.
4
- * Uses esbuild (same bundler as wrangler) with node: builtins as externals.
5
- */
6
- export async function bundleSSRServer(entryPoint) {
7
- const result = await build({
8
- entryPoints: [entryPoint],
9
- bundle: true,
10
- format: "esm",
11
- platform: "neutral",
12
- target: "es2022",
13
- write: false,
14
- minify: false,
15
- external: [
16
- "node:async_hooks",
17
- "node:stream",
18
- "node:stream/web",
19
- "node:buffer",
20
- "node:util",
21
- "node:events",
22
- "node:crypto",
23
- "node:path",
24
- "node:url",
25
- "node:string_decoder",
26
- "node:diagnostics_channel",
27
- "node:process",
28
- "node:fs",
29
- "node:os",
30
- "node:child_process",
31
- "node:http",
32
- "node:https",
33
- "node:net",
34
- "node:tls",
35
- "node:zlib",
36
- "node:perf_hooks",
37
- "node:worker_threads",
38
- ],
39
- conditions: ["workerd", "worker", "import"],
40
- mainFields: ["module", "main"],
41
- logLevel: "warning",
42
- });
43
- if (result.errors.length > 0) {
44
- throw new Error(`esbuild: ${result.errors.map((e) => e.text).join(", ")}`);
45
- }
46
- return result.outputFiles[0].text;
47
- }
48
- //# sourceMappingURL=ssr-bundle.js.map
@@ -1,28 +0,0 @@
1
- /**
2
- * Terms of Service acceptance for CLI.
3
- *
4
- * - First deploy: shows ToS notice + URL + interactive y/N prompt
5
- * - Stored locally in ~/.creek/tos-accepted (version + timestamp)
6
- * - --yes / non-TTY: implicit accept with notice printed
7
- * - Sends tosVersion + tosAcceptedAt in deploy requests
8
- */
9
- export declare const CURRENT_TOS_VERSION = "2026-03-28";
10
- export interface TosAcceptance {
11
- version: string;
12
- acceptedAt: string;
13
- }
14
- /** Read stored ToS acceptance, if any. */
15
- export declare function readTosAcceptance(): TosAcceptance | null;
16
- /** Store ToS acceptance locally. */
17
- export declare function writeTosAcceptance(acceptance: TosAcceptance): void;
18
- /** Check if current ToS version has been accepted. */
19
- export declare function isTosAccepted(): boolean;
20
- /**
21
- * Ensure ToS is accepted before proceeding.
22
- *
23
- * @param autoConfirm - true if --yes flag is set
24
- * @returns TosAcceptance record to send with deploy request
25
- * @throws if user rejects ToS
26
- */
27
- export declare function ensureTosAccepted(autoConfirm: boolean): Promise<TosAcceptance>;
28
- //# sourceMappingURL=tos.d.ts.map
package/dist/utils/tos.js DELETED
@@ -1,95 +0,0 @@
1
- /**
2
- * Terms of Service acceptance for CLI.
3
- *
4
- * - First deploy: shows ToS notice + URL + interactive y/N prompt
5
- * - Stored locally in ~/.creek/tos-accepted (version + timestamp)
6
- * - --yes / non-TTY: implicit accept with notice printed
7
- * - Sends tosVersion + tosAcceptedAt in deploy requests
8
- */
9
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
10
- import { join } from "node:path";
11
- import consola from "consola";
12
- import { getConfigDir } from "./config.js";
13
- import { isTTY } from "./output.js";
14
- const TOS_FILE = join(getConfigDir(), "tos-accepted");
15
- // Bump this when ToS content changes — forces re-acceptance
16
- export const CURRENT_TOS_VERSION = "2026-03-28";
17
- const TOS_URL = "https://creek.dev/legal/terms";
18
- const AUP_URL = "https://creek.dev/legal/acceptable-use";
19
- /** Read stored ToS acceptance, if any. */
20
- export function readTosAcceptance() {
21
- if (!existsSync(TOS_FILE))
22
- return null;
23
- try {
24
- const data = JSON.parse(readFileSync(TOS_FILE, "utf-8"));
25
- if (data.version && data.acceptedAt)
26
- return data;
27
- return null;
28
- }
29
- catch {
30
- return null;
31
- }
32
- }
33
- /** Store ToS acceptance locally. */
34
- export function writeTosAcceptance(acceptance) {
35
- const dir = getConfigDir();
36
- if (!existsSync(dir)) {
37
- mkdirSync(dir, { recursive: true, mode: 0o700 });
38
- }
39
- writeFileSync(TOS_FILE, JSON.stringify(acceptance, null, 2), { mode: 0o600 });
40
- }
41
- /** Check if current ToS version has been accepted. */
42
- export function isTosAccepted() {
43
- const acceptance = readTosAcceptance();
44
- return acceptance?.version === CURRENT_TOS_VERSION;
45
- }
46
- /**
47
- * Ensure ToS is accepted before proceeding.
48
- *
49
- * @param autoConfirm - true if --yes flag is set
50
- * @returns TosAcceptance record to send with deploy request
51
- * @throws if user rejects ToS
52
- */
53
- export async function ensureTosAccepted(autoConfirm) {
54
- // Already accepted current version?
55
- const existing = readTosAcceptance();
56
- if (existing?.version === CURRENT_TOS_VERSION)
57
- return existing;
58
- // Show ToS notice
59
- consola.log("");
60
- consola.log(" By deploying, you agree to Creek's Terms of Service");
61
- consola.log(" and Acceptable Use Policy:");
62
- consola.log(` ${TOS_URL}`);
63
- consola.log(` ${AUP_URL}`);
64
- consola.log("");
65
- if (autoConfirm || !isTTY) {
66
- // Non-interactive: implicit acceptance
67
- consola.log(" Continuing implies acceptance of the above terms.");
68
- consola.log("");
69
- }
70
- else {
71
- // Interactive: ask for explicit acceptance
72
- const readline = await import("node:readline");
73
- const rl = readline.createInterface({
74
- input: process.stdin,
75
- output: process.stdout,
76
- });
77
- const answer = await new Promise((resolve) => {
78
- rl.question(" Accept? [y/N] ", (ans) => {
79
- rl.close();
80
- resolve(ans.trim().toLowerCase());
81
- });
82
- });
83
- if (answer !== "y" && answer !== "yes") {
84
- consola.error("You must accept the Terms of Service to use Creek.");
85
- process.exit(1);
86
- }
87
- }
88
- const acceptance = {
89
- version: CURRENT_TOS_VERSION,
90
- acceptedAt: new Date().toISOString(),
91
- };
92
- writeTosAcceptance(acceptance);
93
- return acceptance;
94
- }
95
- //# sourceMappingURL=tos.js.map