@solcreek/cli 0.3.0

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 (47) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +184 -0
  3. package/dist/commands/claim.d.ts +18 -0
  4. package/dist/commands/claim.js +93 -0
  5. package/dist/commands/deploy.d.ts +38 -0
  6. package/dist/commands/deploy.js +820 -0
  7. package/dist/commands/deployments.d.ts +18 -0
  8. package/dist/commands/deployments.js +84 -0
  9. package/dist/commands/domains.d.ts +2 -0
  10. package/dist/commands/domains.js +159 -0
  11. package/dist/commands/env.d.ts +2 -0
  12. package/dist/commands/env.js +104 -0
  13. package/dist/commands/init.d.ts +18 -0
  14. package/dist/commands/init.js +69 -0
  15. package/dist/commands/login.d.ts +23 -0
  16. package/dist/commands/login.js +120 -0
  17. package/dist/commands/projects.d.ts +13 -0
  18. package/dist/commands/projects.js +38 -0
  19. package/dist/commands/status.d.ts +18 -0
  20. package/dist/commands/status.js +115 -0
  21. package/dist/commands/whoami.d.ts +13 -0
  22. package/dist/commands/whoami.js +43 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.js +38 -0
  25. package/dist/utils/auth-server.d.ts +22 -0
  26. package/dist/utils/auth-server.js +91 -0
  27. package/dist/utils/bundle.d.ts +6 -0
  28. package/dist/utils/bundle.js +39 -0
  29. package/dist/utils/config.d.ts +12 -0
  30. package/dist/utils/config.js +37 -0
  31. package/dist/utils/git-clone.d.ts +44 -0
  32. package/dist/utils/git-clone.js +193 -0
  33. package/dist/utils/nextjs.d.ts +48 -0
  34. package/dist/utils/nextjs.js +368 -0
  35. package/dist/utils/output.d.ts +38 -0
  36. package/dist/utils/output.js +45 -0
  37. package/dist/utils/repo-url.d.ts +48 -0
  38. package/dist/utils/repo-url.js +201 -0
  39. package/dist/utils/sandbox.d.ts +50 -0
  40. package/dist/utils/sandbox.js +69 -0
  41. package/dist/utils/ssr-bundle.d.ts +6 -0
  42. package/dist/utils/ssr-bundle.js +48 -0
  43. package/dist/utils/tos.d.ts +28 -0
  44. package/dist/utils/tos.js +95 -0
  45. package/dist/utils/worker-bundle.d.ts +24 -0
  46. package/dist/utils/worker-bundle.js +136 -0
  47. package/package.json +55 -0
@@ -0,0 +1,18 @@
1
+ export declare const deploymentsCommand: import("citty").CommandDef<{
2
+ json: {
3
+ type: "boolean";
4
+ description: string;
5
+ default: boolean;
6
+ };
7
+ yes: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: boolean;
11
+ };
12
+ project: {
13
+ type: "string";
14
+ description: string;
15
+ required: false;
16
+ };
17
+ }>;
18
+ //# sourceMappingURL=deployments.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 } from "../utils/config.js";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { parseConfig } from "@solcreek/sdk";
8
+ import { globalArgs, resolveJsonMode, jsonOutput } from "../utils/output.js";
9
+ export const deploymentsCommand = defineCommand({
10
+ meta: {
11
+ name: "deployments",
12
+ description: "List recent deployments for the current project",
13
+ },
14
+ args: {
15
+ project: {
16
+ type: "string",
17
+ description: "Project slug (default: from creek.toml)",
18
+ required: false,
19
+ },
20
+ ...globalArgs,
21
+ },
22
+ async run({ args }) {
23
+ const jsonMode = resolveJsonMode(args);
24
+ const token = getToken();
25
+ if (!token) {
26
+ if (jsonMode)
27
+ jsonOutput({ ok: false, error: "not_authenticated" }, 1);
28
+ consola.error("Not authenticated. Run `creek login` first.");
29
+ process.exit(1);
30
+ }
31
+ // Resolve project slug
32
+ let slug = args.project;
33
+ if (!slug) {
34
+ const configPath = join(process.cwd(), "creek.toml");
35
+ if (!existsSync(configPath)) {
36
+ if (jsonMode)
37
+ jsonOutput({ ok: false, error: "no_project", message: "No creek.toml found. Use --project <slug> or run from a project directory." }, 1);
38
+ consola.error("No creek.toml found. Use --project <slug> or run from a project directory.");
39
+ process.exit(1);
40
+ }
41
+ slug = parseConfig(readFileSync(configPath, "utf-8")).project.name;
42
+ }
43
+ const client = new CreekClient(getApiUrl(), token);
44
+ let deployments;
45
+ try {
46
+ deployments = await client.listDeployments(slug);
47
+ }
48
+ catch (err) {
49
+ const msg = err instanceof Error ? err.message : "Failed to list deployments";
50
+ if (jsonMode)
51
+ jsonOutput({ ok: false, error: "api_error", message: msg }, 1);
52
+ consola.error(msg);
53
+ process.exit(1);
54
+ }
55
+ if (jsonMode) {
56
+ jsonOutput({ ok: true, project: slug, deployments });
57
+ }
58
+ if (deployments.length === 0) {
59
+ consola.info(`No deployments for ${slug}. Run \`creek deploy\` to create one.`);
60
+ return;
61
+ }
62
+ consola.info(`${deployments.length} deployment(s) for ${slug}\n`);
63
+ for (const d of deployments) {
64
+ const status = d.status === "active" ? "\x1b[32mactive\x1b[0m" : d.status;
65
+ const branch = d.branch ? ` (${d.branch})` : "";
66
+ const age = timeAgo(d.created_at);
67
+ consola.log(` ${d.id.slice(0, 8)} ${status} ${age}${branch}`);
68
+ }
69
+ },
70
+ });
71
+ function timeAgo(dateStr) {
72
+ const diff = Date.now() - new Date(dateStr).getTime();
73
+ const mins = Math.floor(diff / 60_000);
74
+ if (mins < 1)
75
+ return "just now";
76
+ if (mins < 60)
77
+ return `${mins}m ago`;
78
+ const hours = Math.floor(mins / 60);
79
+ if (hours < 24)
80
+ return `${hours}h ago`;
81
+ const days = Math.floor(hours / 24);
82
+ return `${days}d ago`;
83
+ }
84
+ //# sourceMappingURL=deployments.js.map
@@ -0,0 +1,2 @@
1
+ export declare const domainsCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ //# sourceMappingURL=domains.d.ts.map
@@ -0,0 +1,159 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { CreekClient, parseConfig } 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 { globalArgs, resolveJsonMode, jsonOutput } from "../utils/output.js";
8
+ function getProjectSlug(args) {
9
+ if (args?.project)
10
+ return args.project;
11
+ const configPath = join(process.cwd(), "creek.toml");
12
+ if (!existsSync(configPath)) {
13
+ consola.error("No creek.toml found. Use --project <slug> or run from a project directory.");
14
+ process.exit(1);
15
+ }
16
+ return parseConfig(readFileSync(configPath, "utf-8")).project.name;
17
+ }
18
+ const projectArg = {
19
+ project: { type: "string", description: "Project slug (default: from creek.toml)" },
20
+ };
21
+ function getClient() {
22
+ const token = getToken();
23
+ if (!token) {
24
+ consola.error("Not authenticated. Run `creek login` first.");
25
+ process.exit(1);
26
+ }
27
+ return new CreekClient(getApiUrl(), token);
28
+ }
29
+ const domainsLs = defineCommand({
30
+ meta: { name: "ls", description: "List custom domains" },
31
+ args: {
32
+ ...projectArg,
33
+ ...globalArgs,
34
+ },
35
+ async run({ args }) {
36
+ const jsonMode = resolveJsonMode(args);
37
+ const client = getClient();
38
+ const slug = getProjectSlug(args);
39
+ const domains = await client.listDomains(slug);
40
+ if (jsonMode) {
41
+ jsonOutput({ ok: true, project: slug, domains });
42
+ return;
43
+ }
44
+ if (domains.length === 0) {
45
+ consola.info("No custom domains configured.");
46
+ return;
47
+ }
48
+ for (const d of domains) {
49
+ const statusIcon = d.status === "active" ? "\x1b[32m●\x1b[0m" :
50
+ d.status === "pending" ? "\x1b[33m○\x1b[0m" :
51
+ d.status === "failed" ? "\x1b[31m✕\x1b[0m" : "○";
52
+ consola.log(` ${statusIcon} ${d.hostname} ${d.status} (${d.id.slice(0, 8)})`);
53
+ }
54
+ },
55
+ });
56
+ const domainsAdd = defineCommand({
57
+ meta: { name: "add", description: "Add a custom domain" },
58
+ args: {
59
+ hostname: { type: "positional", description: "Domain to add (e.g., app.example.com)", required: true },
60
+ ...projectArg,
61
+ ...globalArgs,
62
+ },
63
+ async run({ args }) {
64
+ const jsonMode = resolveJsonMode(args);
65
+ const client = getClient();
66
+ const slug = getProjectSlug(args);
67
+ const result = await client.addDomain(slug, args.hostname);
68
+ const { domain, verification } = result;
69
+ if (jsonMode) {
70
+ jsonOutput({ ok: true, project: slug, domain, verification });
71
+ return;
72
+ }
73
+ if (domain.status === "active") {
74
+ consola.success(`Added ${domain.hostname} (active — SSL provisioned)`);
75
+ return;
76
+ }
77
+ consola.success(`Added ${domain.hostname} (status: ${domain.status})`);
78
+ consola.info("");
79
+ consola.info(" Point your DNS to Creek:");
80
+ consola.info(` CNAME ${domain.hostname} → cname.creek.dev`);
81
+ if (verification?.txt) {
82
+ consola.info("");
83
+ consola.info(" Or verify ownership first with a TXT record:");
84
+ consola.info(` TXT ${verification.txt.name} → ${verification.txt.value}`);
85
+ }
86
+ consola.info("");
87
+ consola.info(" Creek will automatically verify and provision SSL.");
88
+ consola.info(` Run \`creek domains ls --project ${slug}\` to check status.`);
89
+ },
90
+ });
91
+ const domainsRm = defineCommand({
92
+ meta: { name: "rm", description: "Remove a custom domain" },
93
+ args: {
94
+ hostname: { type: "positional", description: "Domain to remove", required: true },
95
+ ...projectArg,
96
+ ...globalArgs,
97
+ },
98
+ async run({ args }) {
99
+ const jsonMode = resolveJsonMode(args);
100
+ const client = getClient();
101
+ const slug = getProjectSlug(args);
102
+ // Resolve hostname to domain ID
103
+ const domains = await client.listDomains(slug);
104
+ const domain = domains.find((d) => d.hostname === args.hostname.toLowerCase());
105
+ if (!domain) {
106
+ consola.error(`Domain "${args.hostname}" not found.`);
107
+ process.exit(1);
108
+ }
109
+ await client.deleteDomain(slug, domain.id);
110
+ if (jsonMode) {
111
+ jsonOutput({ ok: true, hostname: domain.hostname, removed: true, project: slug });
112
+ return;
113
+ }
114
+ consola.success(`Removed ${domain.hostname}`);
115
+ },
116
+ });
117
+ const domainsActivate = defineCommand({
118
+ meta: { name: "activate", description: "Activate a pending custom domain" },
119
+ args: {
120
+ hostname: { type: "positional", description: "Domain to activate", required: true },
121
+ ...projectArg,
122
+ ...globalArgs,
123
+ },
124
+ async run({ args }) {
125
+ const jsonMode = resolveJsonMode(args);
126
+ const client = getClient();
127
+ const slug = getProjectSlug(args);
128
+ // Resolve hostname to domain ID
129
+ const domains = await client.listDomains(slug);
130
+ const domain = domains.find((d) => d.hostname === args.hostname.toLowerCase());
131
+ if (!domain) {
132
+ consola.error(`Domain "${args.hostname}" not found.`);
133
+ process.exit(1);
134
+ }
135
+ if (domain.status === "active") {
136
+ consola.info(`${domain.hostname} is already active.`);
137
+ return;
138
+ }
139
+ await client.activateDomain(slug, domain.id);
140
+ if (jsonMode) {
141
+ jsonOutput({ ok: true, hostname: domain.hostname, status: "active", project: slug });
142
+ return;
143
+ }
144
+ consola.success(`Activated ${domain.hostname}`);
145
+ },
146
+ });
147
+ export const domainsCommand = defineCommand({
148
+ meta: {
149
+ name: "domains",
150
+ description: "Manage custom domains",
151
+ },
152
+ subCommands: {
153
+ ls: domainsLs,
154
+ add: domainsAdd,
155
+ rm: domainsRm,
156
+ activate: domainsActivate,
157
+ },
158
+ });
159
+ //# sourceMappingURL=domains.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,104 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { CreekClient, parseConfig } 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 { globalArgs, resolveJsonMode, jsonOutput } from "../utils/output.js";
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
+ ...globalArgs,
30
+ },
31
+ async run({ args }) {
32
+ const jsonMode = resolveJsonMode(args);
33
+ const client = getClient();
34
+ const slug = getProjectSlug();
35
+ await client.setEnvVar(slug, args.key, args.value);
36
+ if (jsonMode)
37
+ jsonOutput({ ok: true, key: args.key, project: slug });
38
+ consola.success(`Set ${args.key}`);
39
+ },
40
+ });
41
+ function redact(value) {
42
+ if (value.length <= 4)
43
+ return "••••";
44
+ return value.slice(0, 2) + "•".repeat(Math.min(value.length - 4, 20)) + value.slice(-2);
45
+ }
46
+ const envGet = defineCommand({
47
+ meta: { name: "ls", description: "List environment variables" },
48
+ args: {
49
+ show: { type: "boolean", description: "Show values in plaintext (default: redacted)", default: false },
50
+ ...globalArgs,
51
+ },
52
+ async run({ args }) {
53
+ const jsonMode = resolveJsonMode(args);
54
+ const client = getClient();
55
+ const slug = getProjectSlug();
56
+ const vars = await client.listEnvVars(slug);
57
+ if (jsonMode) {
58
+ jsonOutput({
59
+ ok: true,
60
+ project: slug,
61
+ vars: vars.map((v) => ({ key: v.key, value: args.show ? v.value : redact(v.value) })),
62
+ });
63
+ }
64
+ if (vars.length === 0) {
65
+ consola.info("No environment variables set.");
66
+ return;
67
+ }
68
+ for (const v of vars) {
69
+ const displayed = args.show ? v.value : redact(v.value);
70
+ consola.log(` ${v.key} = ${displayed}`);
71
+ }
72
+ if (!args.show) {
73
+ consola.info(" (use --show to reveal values)");
74
+ }
75
+ },
76
+ });
77
+ const envRm = defineCommand({
78
+ meta: { name: "rm", description: "Remove an environment variable" },
79
+ args: {
80
+ key: { type: "positional", description: "Variable name to remove", required: true },
81
+ ...globalArgs,
82
+ },
83
+ async run({ args }) {
84
+ const jsonMode = resolveJsonMode(args);
85
+ const client = getClient();
86
+ const slug = getProjectSlug();
87
+ await client.deleteEnvVar(slug, args.key);
88
+ if (jsonMode)
89
+ jsonOutput({ ok: true, key: args.key, removed: true, project: slug });
90
+ consola.success(`Removed ${args.key}`);
91
+ },
92
+ });
93
+ export const envCommand = defineCommand({
94
+ meta: {
95
+ name: "env",
96
+ description: "Manage environment variables",
97
+ },
98
+ subCommands: {
99
+ set: envSet,
100
+ ls: envGet,
101
+ rm: envRm,
102
+ },
103
+ });
104
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1,18 @@
1
+ export declare const initCommand: import("citty").CommandDef<{
2
+ json: {
3
+ type: "boolean";
4
+ description: string;
5
+ default: boolean;
6
+ };
7
+ yes: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: boolean;
11
+ };
12
+ name: {
13
+ type: "string";
14
+ description: string;
15
+ required: false;
16
+ };
17
+ }>;
18
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1,69 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { join, basename } from "node:path";
5
+ import { stringify } from "smol-toml";
6
+ import { detectFramework } from "@solcreek/sdk";
7
+ import { globalArgs, resolveJsonMode, jsonOutput, shouldAutoConfirm } from "../utils/output.js";
8
+ export const initCommand = defineCommand({
9
+ meta: {
10
+ name: "init",
11
+ description: "Initialize a new Creek project",
12
+ },
13
+ args: {
14
+ name: {
15
+ type: "string",
16
+ description: "Project name",
17
+ required: false,
18
+ },
19
+ ...globalArgs,
20
+ },
21
+ async run({ args }) {
22
+ const jsonMode = resolveJsonMode(args);
23
+ const cwd = process.cwd();
24
+ const configPath = join(cwd, "creek.toml");
25
+ if (existsSync(configPath)) {
26
+ if (!shouldAutoConfirm(args)) {
27
+ consola.warn("creek.toml already exists");
28
+ const overwrite = await consola.prompt("Overwrite?", { type: "confirm" });
29
+ if (!overwrite)
30
+ return;
31
+ }
32
+ }
33
+ // Detect framework
34
+ const pkgPath = join(cwd, "package.json");
35
+ let framework;
36
+ if (existsSync(pkgPath)) {
37
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
38
+ const detected = detectFramework(pkg);
39
+ if (detected) {
40
+ framework = detected;
41
+ if (!jsonMode)
42
+ consola.info(`Detected framework: ${framework}`);
43
+ }
44
+ }
45
+ const defaultName = basename(cwd).toLowerCase().replace(/[^a-z0-9-]/g, "-");
46
+ const name = args.name ?? defaultName;
47
+ const config = {
48
+ project: {
49
+ name,
50
+ ...(framework ? { framework } : {}),
51
+ },
52
+ build: {
53
+ command: "npm run build",
54
+ output: "dist",
55
+ },
56
+ resources: {
57
+ d1: false,
58
+ kv: false,
59
+ r2: false,
60
+ },
61
+ };
62
+ writeFileSync(configPath, stringify(config));
63
+ if (jsonMode) {
64
+ jsonOutput({ ok: true, name, framework: framework ?? null, path: configPath });
65
+ }
66
+ consola.success(`Created creek.toml for "${name}"`);
67
+ },
68
+ });
69
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,23 @@
1
+ export declare const loginCommand: import("citty").CommandDef<{
2
+ json: {
3
+ type: "boolean";
4
+ description: string;
5
+ default: boolean;
6
+ };
7
+ yes: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: boolean;
11
+ };
12
+ token: {
13
+ type: "string";
14
+ description: string;
15
+ required: false;
16
+ };
17
+ headless: {
18
+ type: "boolean";
19
+ description: string;
20
+ default: false;
21
+ };
22
+ }>;
23
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1,120 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { execFileSync } from "node:child_process";
4
+ import { CreekClient } from "@solcreek/sdk";
5
+ import { writeCliConfig, readCliConfig, getApiUrl } from "../utils/config.js";
6
+ import { startAuthServer } from "../utils/auth-server.js";
7
+ import { globalArgs, resolveJsonMode, jsonOutput } from "../utils/output.js";
8
+ function getDashboardUrl() {
9
+ const apiUrl = getApiUrl();
10
+ // http://localhost:8787 → http://localhost:3000
11
+ // https://api.creek.dev → https://app.creek.dev
12
+ return apiUrl
13
+ .replace("api.", "app.")
14
+ .replace(":8787", ":3000");
15
+ }
16
+ function openBrowser(url) {
17
+ try {
18
+ const cmd = process.platform === "darwin"
19
+ ? "open"
20
+ : process.platform === "win32"
21
+ ? "start"
22
+ : "xdg-open";
23
+ execFileSync(cmd, [url], { stdio: "ignore" });
24
+ }
25
+ catch {
26
+ // Browser open failed — user will need to copy the URL manually
27
+ }
28
+ }
29
+ export const loginCommand = defineCommand({
30
+ meta: {
31
+ name: "login",
32
+ description: "Authenticate with Creek",
33
+ },
34
+ args: {
35
+ token: {
36
+ type: "string",
37
+ description: "API key (for CI/CD, skips interactive prompt)",
38
+ required: false,
39
+ },
40
+ headless: {
41
+ type: "boolean",
42
+ description: "Use headless mode (paste API key manually, for SSH/remote)",
43
+ default: false,
44
+ },
45
+ ...globalArgs,
46
+ },
47
+ async run({ args }) {
48
+ const jsonMode = resolveJsonMode(args);
49
+ // Mode 1: --token (CI/CD)
50
+ if (args.token) {
51
+ return await saveAndVerify(args.token, jsonMode);
52
+ }
53
+ // Mode 2: --headless (SSH/remote — prompt for API key)
54
+ if (args.headless) {
55
+ return await headlessLogin();
56
+ }
57
+ // Mode 3: Default — localhost redirect (best UX)
58
+ return await browserLogin();
59
+ },
60
+ });
61
+ /**
62
+ * Default login: open browser → dashboard creates API key → redirect to localhost callback.
63
+ */
64
+ async function browserLogin() {
65
+ const { port, state, waitForCallback, close } = startAuthServer();
66
+ const dashboardUrl = getDashboardUrl();
67
+ const authUrl = `${dashboardUrl}/cli-auth?port=${port}&state=${state}`;
68
+ consola.info("Opening browser to authenticate...");
69
+ consola.info(`If the browser doesn't open, visit: ${authUrl}`);
70
+ consola.info("");
71
+ openBrowser(authUrl);
72
+ consola.start("Waiting for authentication...");
73
+ try {
74
+ const key = await waitForCallback();
75
+ await saveAndVerify(key);
76
+ }
77
+ catch (err) {
78
+ close();
79
+ consola.error(err instanceof Error ? err.message : "Authentication failed");
80
+ consola.info("Try `creek login --headless` if browser login isn't working.");
81
+ process.exit(1);
82
+ }
83
+ }
84
+ /**
85
+ * Headless login: prompt user to paste API key from dashboard.
86
+ */
87
+ async function headlessLogin() {
88
+ const dashboardUrl = getDashboardUrl();
89
+ consola.info("Create an API key in the Creek dashboard:");
90
+ consola.info(` ${dashboardUrl}/api-keys`);
91
+ consola.info("");
92
+ const apiKey = await consola.prompt("Paste your API key:", { type: "text" });
93
+ if (!apiKey || typeof apiKey !== "string") {
94
+ consola.error("No API key provided");
95
+ process.exit(1);
96
+ }
97
+ await saveAndVerify(apiKey.trim());
98
+ }
99
+ /**
100
+ * Validate key against API, save to config, print success.
101
+ */
102
+ async function saveAndVerify(apiKey, jsonMode = false) {
103
+ if (!jsonMode)
104
+ consola.start("Verifying...");
105
+ const client = new CreekClient(getApiUrl(), apiKey);
106
+ const session = await client.getSession();
107
+ if (!session?.user) {
108
+ if (jsonMode)
109
+ jsonOutput({ ok: false, error: "invalid_token", message: "Invalid API key" }, 1);
110
+ consola.error("Invalid API key. Please check and try again.");
111
+ process.exit(1);
112
+ }
113
+ const config = readCliConfig();
114
+ writeCliConfig({ ...config, token: apiKey });
115
+ if (jsonMode) {
116
+ jsonOutput({ ok: true, user: session.user.name, email: session.user.email });
117
+ }
118
+ consola.success(`Logged in as ${session.user.name} (${session.user.email})`);
119
+ }
120
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1,13 @@
1
+ export declare const projectsCommand: import("citty").CommandDef<{
2
+ json: {
3
+ type: "boolean";
4
+ description: string;
5
+ default: boolean;
6
+ };
7
+ yes: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: boolean;
11
+ };
12
+ }>;
13
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1,38 @@
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 { globalArgs, resolveJsonMode, jsonOutput } from "../utils/output.js";
6
+ export const projectsCommand = defineCommand({
7
+ meta: {
8
+ name: "projects",
9
+ description: "List all projects",
10
+ },
11
+ args: { ...globalArgs },
12
+ async run({ args }) {
13
+ const jsonMode = resolveJsonMode(args);
14
+ const token = getToken();
15
+ if (!token) {
16
+ if (jsonMode)
17
+ jsonOutput({ ok: false, error: "not_authenticated" }, 1);
18
+ consola.error("Not authenticated. Run `creek login` first.");
19
+ process.exit(1);
20
+ }
21
+ const client = new CreekClient(getApiUrl(), token);
22
+ const projects = await client.listProjects();
23
+ if (jsonMode) {
24
+ jsonOutput({ ok: true, projects });
25
+ }
26
+ if (projects.length === 0) {
27
+ consola.info("No projects yet. Deploy one with `creek deploy`.");
28
+ return;
29
+ }
30
+ consola.info(`${projects.length} project(s)\n`);
31
+ for (const p of projects) {
32
+ const framework = p.framework ? ` (${p.framework})` : "";
33
+ const deployed = p.production_deployment_id ? "deployed" : "not deployed";
34
+ consola.log(` ${p.slug}${framework} — ${deployed}`);
35
+ }
36
+ },
37
+ });
38
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1,18 @@
1
+ export declare const statusCommand: import("citty").CommandDef<{
2
+ json: {
3
+ type: "boolean";
4
+ description: string;
5
+ default: boolean;
6
+ };
7
+ yes: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: boolean;
11
+ };
12
+ id: {
13
+ type: "positional";
14
+ description: string;
15
+ required: false;
16
+ };
17
+ }>;
18
+ //# sourceMappingURL=status.d.ts.map