@solcreek/cli 0.4.21 → 0.4.22

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 (40) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/commands/dashboard.d.ts +21 -0
  3. package/dist/commands/dashboard.js +72 -0
  4. package/dist/commands/deploy.d.ts +10 -0
  5. package/dist/commands/deploy.js +252 -0
  6. package/dist/commands/dev.d.ts +13 -0
  7. package/dist/commands/dev.js +77 -2
  8. package/dist/commands/init.d.ts +10 -0
  9. package/dist/commands/init.js +158 -2
  10. package/dist/commands/logs.d.ts +12 -0
  11. package/dist/commands/logs.js +69 -1
  12. package/dist/commands/restart.d.ts +26 -0
  13. package/dist/commands/restart.js +55 -0
  14. package/dist/commands/rollback.d.ts +13 -0
  15. package/dist/commands/rollback.js +188 -1
  16. package/dist/commands/stop.d.ts +26 -0
  17. package/dist/commands/stop.js +65 -0
  18. package/dist/commands/top.d.ts +28 -0
  19. package/dist/commands/top.js +171 -0
  20. package/dist/dev/creekd-runner.d.ts +22 -0
  21. package/dist/dev/creekd-runner.js +188 -0
  22. package/dist/index.js +8 -0
  23. package/dist/utils/creekd-client.d.ts +152 -0
  24. package/dist/utils/creekd-client.js +144 -0
  25. package/dist/utils/gitignore.d.ts +2 -0
  26. package/dist/utils/gitignore.js +32 -0
  27. package/dist/utils/hostkey.d.ts +39 -0
  28. package/dist/utils/hostkey.js +84 -0
  29. package/dist/utils/hosts.d.ts +70 -0
  30. package/dist/utils/hosts.js +90 -0
  31. package/dist/utils/local-cache.d.ts +69 -0
  32. package/dist/utils/local-cache.js +100 -0
  33. package/dist/utils/nextjs.d.ts +4 -2
  34. package/dist/utils/nextjs.js +107 -38
  35. package/dist/utils/prepare-bundle.js +1 -1
  36. package/dist/utils/top-format.d.ts +4 -0
  37. package/dist/utils/top-format.js +32 -0
  38. package/dist/utils/watch.d.ts +81 -0
  39. package/dist/utils/watch.js +87 -0
  40. package/package.json +2 -2
@@ -0,0 +1,65 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { globalArgs, resolveJsonMode, jsonOutput, shouldAutoConfirm } from "../utils/output.js";
4
+ import { CreekdClient, CreekdApiError, getCreekdUrl } from "../utils/creekd-client.js";
5
+ export const stopCommand = defineCommand({
6
+ meta: {
7
+ name: "stop",
8
+ description: "Stop an app on a creekd instance",
9
+ },
10
+ args: {
11
+ id: {
12
+ type: "positional",
13
+ description: "App ID to stop",
14
+ required: true,
15
+ },
16
+ server: {
17
+ type: "string",
18
+ description: "creekd admin API URL (or $CREEKD_URL)",
19
+ },
20
+ token: {
21
+ type: "string",
22
+ description: "Bearer token (or $CREEKD_TOKEN)",
23
+ },
24
+ ...globalArgs,
25
+ },
26
+ async run({ args }) {
27
+ const jsonMode = resolveJsonMode(args);
28
+ const client = new CreekdClient(args.server || getCreekdUrl(), args.token || process.env.CREEKD_TOKEN || process.env.CREEKCTL_TOKEN || "");
29
+ const id = args.id;
30
+ if (!shouldAutoConfirm(args) && !jsonMode) {
31
+ const readline = await import("node:readline/promises");
32
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
33
+ const answer = await rl.question(`Stop app "${id}"? [y/N] `);
34
+ rl.close();
35
+ if (answer.toLowerCase() !== "y") {
36
+ consola.info("Aborted.");
37
+ return;
38
+ }
39
+ }
40
+ try {
41
+ await client.stopApp(id);
42
+ if (jsonMode) {
43
+ jsonOutput({ ok: true, stopped: id }, 0, [
44
+ { command: `creek top`, description: "Live process overview" },
45
+ ]);
46
+ }
47
+ consola.success(`Stopped ${id}`);
48
+ }
49
+ catch (err) {
50
+ if (err instanceof CreekdApiError) {
51
+ if (jsonMode)
52
+ jsonOutput({ ok: false, error: err.code, message: err.message }, 1);
53
+ if (err.status === 404) {
54
+ consola.error(`App "${id}" not found.`);
55
+ }
56
+ else {
57
+ consola.error(`Stop failed: ${err.message}`);
58
+ }
59
+ process.exit(1);
60
+ }
61
+ throw err;
62
+ }
63
+ },
64
+ });
65
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1,28 @@
1
+ export declare const topCommand: import("citty").CommandDef<{
2
+ server: {
3
+ type: "string";
4
+ description: string;
5
+ required: false;
6
+ };
7
+ token: {
8
+ type: "string";
9
+ description: string;
10
+ required: false;
11
+ };
12
+ interval: {
13
+ type: "string";
14
+ description: string;
15
+ default: string;
16
+ };
17
+ json: {
18
+ type: "boolean";
19
+ description: string;
20
+ default: boolean;
21
+ };
22
+ yes: {
23
+ type: "boolean";
24
+ description: string;
25
+ default: boolean;
26
+ };
27
+ }>;
28
+ //# sourceMappingURL=top.d.ts.map
@@ -0,0 +1,171 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { globalArgs, resolveJsonMode, jsonOutput, isTTY } from "../utils/output.js";
4
+ import { CreekdClient, CreekdApiError, getCreekdUrl, } from "../utils/creekd-client.js";
5
+ import { fmtBytes, fmtDuration, calcCpuPercent } from "../utils/top-format.js";
6
+ export const topCommand = defineCommand({
7
+ meta: {
8
+ name: "top",
9
+ description: "Live view of apps on a creekd instance",
10
+ },
11
+ args: {
12
+ ...globalArgs,
13
+ server: {
14
+ type: "string",
15
+ description: "creekd admin API URL (or $CREEKD_URL)",
16
+ required: false,
17
+ },
18
+ token: {
19
+ type: "string",
20
+ description: "Bearer token (or $CREEKD_TOKEN)",
21
+ required: false,
22
+ },
23
+ interval: {
24
+ type: "string",
25
+ description: "Refresh interval in seconds (default 2)",
26
+ default: "2",
27
+ },
28
+ },
29
+ async run({ args }) {
30
+ const jsonMode = resolveJsonMode(args);
31
+ const client = new CreekdClient(args.server || getCreekdUrl(), args.token || process.env.CREEKD_TOKEN || process.env.CREEKCTL_TOKEN || "");
32
+ const intervalMs = Math.max(500, parseFloat(args.interval || "2") * 1000);
33
+ if (jsonMode) {
34
+ const snapshot = await collectSnapshot(client);
35
+ jsonOutput({ ok: true, ...snapshot }, 0, [
36
+ { command: "creek top --json", description: "Refresh snapshot" },
37
+ { command: "creek logs <app-id>", description: "Stream app logs" },
38
+ ]);
39
+ }
40
+ await liveTop(client, intervalMs);
41
+ },
42
+ });
43
+ let prevCpu = new Map();
44
+ async function collectSnapshot(client) {
45
+ const apps = await client.listApps();
46
+ const now = Date.now();
47
+ const rows = [];
48
+ const statsResults = await Promise.allSettled(apps.map((app) => client.getStats(app.id)));
49
+ for (let i = 0; i < apps.length; i++) {
50
+ const app = apps[i];
51
+ const stats = statsResults[i].status === "fulfilled"
52
+ ? statsResults[i].value
53
+ : null;
54
+ let cpuStr = "—";
55
+ if (stats?.cgroup_enabled && stats.cpu_usage_usec != null) {
56
+ const prev = prevCpu.get(app.id);
57
+ if (prev) {
58
+ const pct = calcCpuPercent(prev.usec, prev.ts, stats.cpu_usage_usec, now);
59
+ if (pct !== null)
60
+ cpuStr = pct.toFixed(1) + "%";
61
+ }
62
+ prevCpu.set(app.id, { usec: stats.cpu_usage_usec, ts: now });
63
+ }
64
+ rows.push({
65
+ id: app.id,
66
+ status: app.status,
67
+ cpu: cpuStr,
68
+ mem: stats?.memory_current_bytes != null ? fmtBytes(stats.memory_current_bytes) : "—",
69
+ memLimit: stats?.memory_max_bytes != null && stats.memory_max_bytes > 0
70
+ ? fmtBytes(stats.memory_max_bytes)
71
+ : "—",
72
+ pids: stats?.pids_current != null ? String(stats.pids_current) : "—",
73
+ restarts: app.restart_count,
74
+ uptime: fmtDuration(app.uptime_ms),
75
+ });
76
+ }
77
+ const running = apps.filter((a) => a.status === "running").length;
78
+ const crashed = apps.filter((a) => a.status === "crash_loop").length;
79
+ return {
80
+ apps: rows,
81
+ summary: { total: apps.length, running, crashed },
82
+ timestamp: new Date().toISOString(),
83
+ };
84
+ }
85
+ async function liveTop(client, intervalMs) {
86
+ const url = client instanceof CreekdClient
87
+ ? getCreekdUrl()
88
+ : "creekd";
89
+ let first = true;
90
+ // eslint-disable-next-line no-constant-condition
91
+ while (true) {
92
+ try {
93
+ const snap = await collectSnapshot(client);
94
+ if (isTTY)
95
+ process.stdout.write("\x1b[2J\x1b[H");
96
+ render(snap, url);
97
+ first = false;
98
+ }
99
+ catch (err) {
100
+ if (first) {
101
+ if (err instanceof CreekdApiError && err.status === 401) {
102
+ consola.error("Authentication failed. Set CREEKD_TOKEN or use --token.");
103
+ }
104
+ else {
105
+ consola.error(`Cannot reach creekd at ${url}`);
106
+ consola.info("Is creekd running? Check with: systemctl status creekd");
107
+ }
108
+ process.exit(1);
109
+ }
110
+ if (isTTY)
111
+ process.stdout.write("\x1b[2J\x1b[H");
112
+ consola.warn(`Refresh failed: ${err.message}`);
113
+ }
114
+ await sleep(intervalMs);
115
+ }
116
+ }
117
+ function render(snap, url) {
118
+ const { apps, summary } = snap;
119
+ const dim = "\x1b[2m";
120
+ const reset = "\x1b[0m";
121
+ const bold = "\x1b[1m";
122
+ const green = "\x1b[32m";
123
+ const red = "\x1b[31m";
124
+ const yellow = "\x1b[33m";
125
+ const header = `${bold}creek top${reset}${dim} — ${url}${reset} ` +
126
+ `${summary.total} apps, ${green}${summary.running} running${reset}` +
127
+ (summary.crashed > 0 ? `, ${red}${summary.crashed} crashed${reset}` : "");
128
+ process.stdout.write(header + "\n\n");
129
+ if (apps.length === 0) {
130
+ process.stdout.write(`${dim} No apps running.${reset}\n`);
131
+ return;
132
+ }
133
+ const cols = ["APP", "STATUS", "CPU", "MEM", "LIMIT", "PIDS", "RESTARTS", "UPTIME"];
134
+ const widths = cols.map((c, i) => {
135
+ const dataMax = Math.max(...apps.map((r) => String(cellValue(r, i)).length), 0);
136
+ return Math.max(c.length, dataMax);
137
+ });
138
+ const headerLine = cols.map((c, i) => c.padEnd(widths[i])).join(" ");
139
+ process.stdout.write(`${dim} ${headerLine}${reset}\n`);
140
+ for (const row of apps) {
141
+ const statusColor = row.status === "running" ? green
142
+ : row.status === "crash_loop" ? red
143
+ : row.status === "starting" ? yellow
144
+ : dim;
145
+ const cells = cols.map((_, i) => {
146
+ const val = String(cellValue(row, i));
147
+ if (i === 1)
148
+ return `${statusColor}${val.padEnd(widths[i])}${reset}`;
149
+ return val.padEnd(widths[i]);
150
+ });
151
+ process.stdout.write(" " + cells.join(" ") + "\n");
152
+ }
153
+ process.stdout.write(`\n${dim} Refreshing every ${snap._intervalS || 2}s — Ctrl+C to quit${reset}\n`);
154
+ }
155
+ function cellValue(row, col) {
156
+ switch (col) {
157
+ case 0: return row.id;
158
+ case 1: return row.status;
159
+ case 2: return row.cpu;
160
+ case 3: return row.mem;
161
+ case 4: return row.memLimit;
162
+ case 5: return row.pids;
163
+ case 6: return row.restarts;
164
+ case 7: return row.uptime;
165
+ default: return "";
166
+ }
167
+ }
168
+ function sleep(ms) {
169
+ return new Promise((resolve) => setTimeout(resolve, ms));
170
+ }
171
+ //# sourceMappingURL=top.js.map
@@ -0,0 +1,22 @@
1
+ import type { ResolvedConfig } from "@solcreek/sdk";
2
+ export interface CreekdDevServerOptions {
3
+ cwd: string;
4
+ port: number;
5
+ config: ResolvedConfig;
6
+ reset: boolean;
7
+ }
8
+ export declare class CreekdDevServer {
9
+ private options;
10
+ private sandboxProcess;
11
+ constructor(options: CreekdDevServerOptions);
12
+ start(): Promise<void>;
13
+ stop(): Promise<void>;
14
+ private requireCreekd;
15
+ private ensureSandbox;
16
+ private buildEnvVars;
17
+ private detectDevCommand;
18
+ /** For compatibility with DevServer interface */
19
+ triggerScheduled(): Promise<void>;
20
+ sendQueueMessage(_payload: unknown): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=creekd-runner.d.ts.map
@@ -0,0 +1,188 @@
1
+ // CreekdDevServer for `creek dev --target creekd`.
2
+ //
3
+ // Orchestrates `creekd sandbox` (Go binary) instead of Miniflare.
4
+ // Real Postgres, Redis, SeaweedFS run inside a Lima VM.
5
+ // The app process runs inside the VM with env vars injected.
6
+ //
7
+ // Requirements:
8
+ // - `creekd` binary in PATH
9
+ // - Lima (`limactl`) for macOS/Linux sandbox VM
10
+ import { execSync, spawn } from "node:child_process";
11
+ import { existsSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { consola } from "consola";
14
+ export class CreekdDevServer {
15
+ options;
16
+ sandboxProcess = null;
17
+ constructor(options) {
18
+ this.options = options;
19
+ }
20
+ async start() {
21
+ const { cwd, port, config, reset } = this.options;
22
+ const startTime = Date.now();
23
+ // 1. Check creekd is installed
24
+ this.requireCreekd();
25
+ // 2. Start sandbox (provisions Lima VM + primitives from creek.toml)
26
+ consola.info("Starting creekd sandbox...");
27
+ const status = await this.ensureSandbox(cwd);
28
+ // 3. Build env var map from sandbox primitives
29
+ const env = this.buildEnvVars(config, status, port);
30
+ // 4. Load .env.local if present
31
+ const envLocalPath = join(cwd, ".env.local");
32
+ if (existsSync(envLocalPath)) {
33
+ const { readFileSync } = await import("node:fs");
34
+ const lines = readFileSync(envLocalPath, "utf-8").split("\n");
35
+ for (const line of lines) {
36
+ const trimmed = line.trim();
37
+ if (!trimmed || trimmed.startsWith("#"))
38
+ continue;
39
+ const eqIdx = trimmed.indexOf("=");
40
+ if (eqIdx > 0) {
41
+ const key = trimmed.slice(0, eqIdx);
42
+ const val = trimmed.slice(eqIdx + 1).replace(/^["']|["']$/g, "");
43
+ if (!env[key])
44
+ env[key] = val;
45
+ }
46
+ }
47
+ consola.info(`.env.local loaded`);
48
+ }
49
+ // 5. Detect runtime and dev command
50
+ const devCmd = this.detectDevCommand(cwd, config);
51
+ // 6. Print status
52
+ const elapsed = Date.now() - startTime;
53
+ console.log("");
54
+ consola.success("⬡ creek dev (creekd sandbox)\n");
55
+ consola.info(`App: http://localhost:${port}`);
56
+ for (const p of status.ports) {
57
+ if (p.name !== "app") {
58
+ consola.info(`${p.name.padEnd(12)}localhost:${p.host}`);
59
+ }
60
+ }
61
+ console.log("");
62
+ for (const [k, v] of Object.entries(env)) {
63
+ if (k.startsWith("DATABASE") || k.startsWith("REDIS") || k.startsWith("S3_") || k.startsWith("SMTP")) {
64
+ const masked = v.replace(/:[^:@]+@/, ":***@");
65
+ consola.info(` ${k}=${masked}`);
66
+ }
67
+ }
68
+ console.log("");
69
+ consola.info(`Running: ${devCmd.join(" ")}`);
70
+ consola.info(`Ready in ${elapsed}ms`);
71
+ console.log("");
72
+ // 7. Start the dev server process with env vars
73
+ this.sandboxProcess = spawn(devCmd[0], devCmd.slice(1), {
74
+ cwd,
75
+ env: { ...process.env, ...env },
76
+ stdio: "inherit",
77
+ });
78
+ this.sandboxProcess.on("exit", (code) => {
79
+ if (code !== null && code !== 0) {
80
+ consola.error(`Dev server exited with code ${code}`);
81
+ }
82
+ });
83
+ }
84
+ async stop() {
85
+ if (this.sandboxProcess) {
86
+ this.sandboxProcess.kill("SIGTERM");
87
+ this.sandboxProcess = null;
88
+ }
89
+ // Don't stop the sandbox VM — it persists for fast restarts
90
+ }
91
+ // --- Internals ---
92
+ requireCreekd() {
93
+ try {
94
+ execSync("creekd --version", { stdio: "pipe" });
95
+ }
96
+ catch {
97
+ throw new Error([
98
+ "creekd is not installed.",
99
+ "",
100
+ " Install with: curl -fsSL https://install.creek.dev | sh",
101
+ "",
102
+ " Or use --target cf to develop with Miniflare (CF Workers local).",
103
+ ].join("\n"));
104
+ }
105
+ }
106
+ async ensureSandbox(cwd) {
107
+ try {
108
+ const output = execSync(`creekd sandbox --non-interactive --json "${cwd}"`, { encoding: "utf-8", timeout: 300_000 });
109
+ // Find the JSON line in output (creekd may print logs before JSON)
110
+ const lines = output.trim().split("\n");
111
+ for (let i = lines.length - 1; i >= 0; i--) {
112
+ try {
113
+ return JSON.parse(lines[i]);
114
+ }
115
+ catch {
116
+ continue;
117
+ }
118
+ }
119
+ throw new Error("No JSON status from creekd sandbox");
120
+ }
121
+ catch (e) {
122
+ if (e.message?.includes("not installed"))
123
+ throw e;
124
+ throw new Error(`creekd sandbox failed: ${e.message}`);
125
+ }
126
+ }
127
+ buildEnvVars(config, status, port) {
128
+ const env = {
129
+ PORT: String(port),
130
+ };
131
+ // Map sandbox ports to standard env vars
132
+ for (const p of status.ports) {
133
+ switch (p.name) {
134
+ case "postgres":
135
+ env.DATABASE_URL = `postgresql://creek:creek_sandbox@127.0.0.1:${p.host}/app`;
136
+ break;
137
+ case "mysql":
138
+ env.DATABASE_URL = `mysql://creek:creek_sandbox@127.0.0.1:${p.host}/app`;
139
+ break;
140
+ case "redis":
141
+ env.REDIS_URL = `redis://127.0.0.1:${p.host}/0`;
142
+ break;
143
+ case "s3":
144
+ env.S3_ENDPOINT = `http://127.0.0.1:${p.host}`;
145
+ env.S3_BUCKET = config.projectName;
146
+ env.AWS_ACCESS_KEY_ID = "creek";
147
+ env.AWS_SECRET_ACCESS_KEY = "creek_sandbox";
148
+ break;
149
+ case "smtp":
150
+ env.SMTP_URL = `smtp://127.0.0.1:${p.host}`;
151
+ break;
152
+ }
153
+ }
154
+ // SQLite fallback for database if no postgres/mysql port
155
+ if (!env.DATABASE_URL) {
156
+ const dbDir = join(this.options.cwd, ".creek", "dev");
157
+ env.DATABASE_URL = `sqlite://${dbDir}/dev.db`;
158
+ }
159
+ return env;
160
+ }
161
+ detectDevCommand(cwd, config) {
162
+ // Check package.json for dev script
163
+ const pkgPath = join(cwd, "package.json");
164
+ if (existsSync(pkgPath)) {
165
+ const { readFileSync } = require("node:fs");
166
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
167
+ if (pkg.scripts?.dev) {
168
+ return ["npm", "run", "dev"];
169
+ }
170
+ }
171
+ // Fallback to bun --watch or node --watch
172
+ const entryFiles = ["src/index.ts", "src/index.mjs", "src/index.js", "index.ts", "index.mjs", "index.js"];
173
+ for (const entry of entryFiles) {
174
+ if (existsSync(join(cwd, entry))) {
175
+ return ["bun", "--watch", entry];
176
+ }
177
+ }
178
+ return ["bun", "--watch", "."];
179
+ }
180
+ /** For compatibility with DevServer interface */
181
+ async triggerScheduled() {
182
+ consola.warn("Scheduled triggers not yet supported in creekd dev mode");
183
+ }
184
+ async sendQueueMessage(_payload) {
185
+ consola.warn("Queue messages not yet supported in creekd dev mode");
186
+ }
187
+ }
188
+ //# sourceMappingURL=creekd-runner.js.map
package/dist/index.js CHANGED
@@ -24,6 +24,10 @@ import { doctorCommand } from "./commands/doctor.js";
24
24
  import { dbCommand } from "./commands/db.js";
25
25
  import { storageCommand } from "./commands/storage.js";
26
26
  import { cacheCommand } from "./commands/cache.js";
27
+ import { topCommand } from "./commands/top.js";
28
+ import { restartCommand } from "./commands/restart.js";
29
+ import { stopCommand } from "./commands/stop.js";
30
+ import { dashboardCommand } from "./commands/dashboard.js";
27
31
  const __dirname = dirname(fileURLToPath(import.meta.url));
28
32
  const cliPkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
29
33
  // Read version from the "creek" facade package (what users install),
@@ -63,6 +67,10 @@ const main = defineCommand({
63
67
  cache: cacheCommand,
64
68
  domains: domainsCommand,
65
69
  rollback: rollbackCommand,
70
+ top: topCommand,
71
+ dashboard: dashboardCommand,
72
+ restart: restartCommand,
73
+ stop: stopCommand,
66
74
  ops: opsCommand,
67
75
  },
68
76
  });
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Lightweight creekd admin API client for the CLI.
3
+ *
4
+ * Uses plain fetch (no openapi-fetch dep) with types matching
5
+ * the OpenAPI spec. The CLI only needs a handful of endpoints.
6
+ */
7
+ export interface AppView {
8
+ id: string;
9
+ runtime?: string;
10
+ command: string;
11
+ args?: string[];
12
+ env?: string[];
13
+ port: number;
14
+ status: "starting" | "running" | "crash_loop" | "stopping" | "stopped";
15
+ pid: number;
16
+ uptime_ms: number;
17
+ restart_count: number;
18
+ health_failures: number;
19
+ net_ip?: string;
20
+ }
21
+ export interface StatsView {
22
+ id: string;
23
+ cgroup_enabled: boolean;
24
+ memory_current_bytes?: number;
25
+ memory_max_bytes?: number;
26
+ pids_current?: number;
27
+ cpu_usage_usec?: number;
28
+ oom_kills?: number;
29
+ read_err?: string;
30
+ }
31
+ export interface AppEnvelope {
32
+ apiVersion: string;
33
+ kind: string;
34
+ metadata: {
35
+ name: string;
36
+ uid: string;
37
+ generation: number;
38
+ resourceVersion: string;
39
+ creationTimestamp: string;
40
+ };
41
+ spec: {
42
+ runtime?: string;
43
+ command?: string;
44
+ args?: string[];
45
+ env?: string[];
46
+ port?: number;
47
+ };
48
+ status: {
49
+ observedGeneration: number;
50
+ conditions: Array<{
51
+ type: string;
52
+ status: string;
53
+ lastTransitionTime: string;
54
+ reason: string;
55
+ message?: string;
56
+ }>;
57
+ currentPid: number;
58
+ currentPort: number;
59
+ restartCount: number;
60
+ healthFailures: number;
61
+ uptimeMs: number;
62
+ };
63
+ }
64
+ export interface ListAppsResponse {
65
+ apps: AppView[];
66
+ }
67
+ /** Release ledger entry returned by POST /v1/apps/{id}/rollback. */
68
+ export interface Release {
69
+ uid: string;
70
+ phase: "Active" | "Superseded" | "RolledBack";
71
+ creationTimestamp: string;
72
+ spec: {
73
+ appUid: string;
74
+ releaseSeq: number;
75
+ gitSha?: string;
76
+ image?: string;
77
+ envHash?: string;
78
+ createdBy?: string;
79
+ rolledBackFrom?: number;
80
+ originalArtifactRelease?: number;
81
+ };
82
+ }
83
+ export interface ErrorResponse {
84
+ code: string;
85
+ error: string;
86
+ }
87
+ export declare class CreekdApiError extends Error {
88
+ status: number;
89
+ code: string;
90
+ constructor(status: number, code: string);
91
+ }
92
+ /**
93
+ * Thrown specifically on 412 Precondition Failed (If-Match
94
+ * mismatch). Carries the daemon's CURRENT rv so the caller can
95
+ * decide between (a) prompting the user to refresh, (b) auto-
96
+ * retrying with the fresh rv when --bypass-rv was passed, or
97
+ * (c) emitting a structured machine-readable error to JSON mode.
98
+ *
99
+ * Per DESIGN-self-host-state.md §"First-party CLI MUST send
100
+ * If-Match": "On 412, CLI surfaces a structured prompt and does
101
+ * NOT auto-retry by default."
102
+ */
103
+ export declare class CreekdResourceVersionMismatchError extends CreekdApiError {
104
+ currentResourceVersion: string;
105
+ attemptedResourceVersion: string;
106
+ constructor(currentResourceVersion: string, attemptedResourceVersion: string);
107
+ }
108
+ /** Options that apply to every mutating call. */
109
+ export interface MutateOptions {
110
+ /**
111
+ * If-Match value to send as the precondition header. Daemon
112
+ * returns 412 → CreekdResourceVersionMismatchError if it doesn't
113
+ * match the daemon's current rv. Omit (or pass undefined) for
114
+ * unconditional writes; the daemon then attaches
115
+ * `Warning: 299 - "unconditional-write"` to the response.
116
+ */
117
+ ifMatch?: string;
118
+ }
119
+ export declare function getCreekdUrl(): string;
120
+ export declare function getCreekdToken(): string;
121
+ export declare class CreekdClient {
122
+ private token;
123
+ private baseUrl;
124
+ constructor(baseUrl?: string, token?: string);
125
+ listApps(): Promise<AppView[]>;
126
+ getApp(id: string): Promise<AppEnvelope>;
127
+ getStats(id: string): Promise<StatsView>;
128
+ getAppLogs(id: string, tail?: number): Promise<string>;
129
+ stopApp(id: string, opts?: MutateOptions): Promise<void>;
130
+ /**
131
+ * Spawn a brand-new app. POST /v1/apps. Creation is not
132
+ * spec-mutating in the rv sense — there's no prior version to
133
+ * If-Match against — so ifMatch is intentionally NOT a parameter.
134
+ */
135
+ spawnApp(body: unknown): Promise<AppView>;
136
+ /**
137
+ * Blue-green deploy of an existing app. POST /v1/apps/{id}/deploy.
138
+ * Spec-mutating; pass ifMatch sourced from the local cache (or a
139
+ * fresh getApp) — 412 surfaces as CreekdResourceVersionMismatchError.
140
+ */
141
+ deployApp(id: string, body: unknown, opts?: MutateOptions): Promise<AppView>;
142
+ restartApp(id: string): Promise<AppView>;
143
+ /**
144
+ * Roll back to the target release seq. Spec-mutating — accepts
145
+ * If-Match. Throws CreekdResourceVersionMismatchError on 412.
146
+ */
147
+ rollbackApp(id: string, toSeq: number, opts?: MutateOptions): Promise<Release>;
148
+ private get;
149
+ private post;
150
+ private request;
151
+ }
152
+ //# sourceMappingURL=creekd-client.d.ts.map