@prajwolkc/stk 0.1.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.
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/commands/deploy.d.ts +2 -0
- package/dist/commands/deploy.js +152 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.js +77 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +111 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +151 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +130 -0
- package/dist/commands/todo.d.ts +2 -0
- package/dist/commands/todo.js +187 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +22 -0
- package/dist/lib/config.d.ts +36 -0
- package/dist/lib/config.js +102 -0
- package/dist/services/aws.d.ts +2 -0
- package/dist/services/aws.js +32 -0
- package/dist/services/checker.d.ts +10 -0
- package/dist/services/checker.js +20 -0
- package/dist/services/database.d.ts +2 -0
- package/dist/services/database.js +27 -0
- package/dist/services/fly.d.ts +2 -0
- package/dist/services/fly.js +17 -0
- package/dist/services/mongodb.d.ts +2 -0
- package/dist/services/mongodb.js +25 -0
- package/dist/services/r2.d.ts +2 -0
- package/dist/services/r2.js +20 -0
- package/dist/services/railway.d.ts +2 -0
- package/dist/services/railway.js +26 -0
- package/dist/services/redis.d.ts +2 -0
- package/dist/services/redis.js +34 -0
- package/dist/services/registry.d.ts +4 -0
- package/dist/services/registry.js +37 -0
- package/dist/services/render.d.ts +2 -0
- package/dist/services/render.js +19 -0
- package/dist/services/stripe.d.ts +2 -0
- package/dist/services/stripe.js +21 -0
- package/dist/services/supabase.d.ts +2 -0
- package/dist/services/supabase.js +24 -0
- package/dist/services/vercel.d.ts +2 -0
- package/dist/services/vercel.js +35 -0
- package/dist/templates/index.d.ts +8 -0
- package/dist/templates/index.js +105 -0
- package/package.json +55 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CheckResult {
|
|
2
|
+
name: string;
|
|
3
|
+
status: "healthy" | "degraded" | "down" | "skipped";
|
|
4
|
+
latency?: number;
|
|
5
|
+
detail?: string;
|
|
6
|
+
}
|
|
7
|
+
export type ServiceChecker = () => Promise<CheckResult>;
|
|
8
|
+
export declare function runCheck(name: string, fn: () => Promise<{
|
|
9
|
+
detail?: string;
|
|
10
|
+
}>): Promise<CheckResult>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export async function runCheck(name, fn) {
|
|
2
|
+
const start = Date.now();
|
|
3
|
+
try {
|
|
4
|
+
const result = await fn();
|
|
5
|
+
return {
|
|
6
|
+
name,
|
|
7
|
+
status: "healthy",
|
|
8
|
+
latency: Date.now() - start,
|
|
9
|
+
detail: result.detail,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
status: "down",
|
|
16
|
+
latency: Date.now() - start,
|
|
17
|
+
detail: err.message ?? String(err),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkDatabase() {
|
|
3
|
+
const url = process.env.DATABASE_URL;
|
|
4
|
+
if (!url) {
|
|
5
|
+
return { name: "PostgreSQL", status: "skipped", detail: "DATABASE_URL not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("PostgreSQL", async () => {
|
|
8
|
+
// Parse host and port from DATABASE_URL to do a basic TCP connect check
|
|
9
|
+
// Full query check would require pg client — keeping deps minimal for now
|
|
10
|
+
const parsed = new URL(url);
|
|
11
|
+
const host = parsed.hostname;
|
|
12
|
+
const port = parseInt(parsed.port || "5432", 10);
|
|
13
|
+
const { createConnection } = await import("net");
|
|
14
|
+
await new Promise((resolve, reject) => {
|
|
15
|
+
const socket = createConnection({ host, port, timeout: 5000 }, () => {
|
|
16
|
+
socket.destroy();
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
|
19
|
+
socket.on("error", reject);
|
|
20
|
+
socket.on("timeout", () => {
|
|
21
|
+
socket.destroy();
|
|
22
|
+
reject(new Error("connection timeout"));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
return { detail: `${host}:${port} reachable` };
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkFly() {
|
|
3
|
+
const token = process.env.FLY_API_TOKEN;
|
|
4
|
+
if (!token) {
|
|
5
|
+
return { name: "Fly.io", status: "skipped", detail: "FLY_API_TOKEN not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("Fly.io", async () => {
|
|
8
|
+
const res = await fetch("https://api.machines.dev/v1/apps", {
|
|
9
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new Error(`HTTP ${res.status}`);
|
|
13
|
+
const data = (await res.json());
|
|
14
|
+
const apps = data.total_apps ?? data.length ?? 0;
|
|
15
|
+
return { detail: `${apps} app${apps !== 1 ? "s" : ""} found` };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkMongoDB() {
|
|
3
|
+
const url = process.env.MONGODB_URL ?? process.env.MONGO_URL;
|
|
4
|
+
if (!url) {
|
|
5
|
+
return { name: "MongoDB", status: "skipped", detail: "MONGODB_URL not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("MongoDB", async () => {
|
|
8
|
+
const parsed = new URL(url);
|
|
9
|
+
const host = parsed.hostname;
|
|
10
|
+
const port = parseInt(parsed.port || "27017", 10);
|
|
11
|
+
const { createConnection } = await import("net");
|
|
12
|
+
await new Promise((resolve, reject) => {
|
|
13
|
+
const socket = createConnection({ host, port, timeout: 5000 }, () => {
|
|
14
|
+
socket.destroy();
|
|
15
|
+
resolve();
|
|
16
|
+
});
|
|
17
|
+
socket.on("error", reject);
|
|
18
|
+
socket.on("timeout", () => {
|
|
19
|
+
socket.destroy();
|
|
20
|
+
reject(new Error("connection timeout"));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
return { detail: `${host}:${port} reachable` };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkR2() {
|
|
3
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
4
|
+
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
5
|
+
if (!accountId || !apiToken) {
|
|
6
|
+
return {
|
|
7
|
+
name: "R2 Storage",
|
|
8
|
+
status: "skipped",
|
|
9
|
+
detail: "CLOUDFLARE_ACCOUNT_ID or CLOUDFLARE_API_TOKEN not set",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return runCheck("R2 Storage", async () => {
|
|
13
|
+
const res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/r2/buckets`, { headers: { Authorization: `Bearer ${apiToken}` } });
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
throw new Error(`HTTP ${res.status}`);
|
|
16
|
+
const data = await res.json();
|
|
17
|
+
const count = data.result?.length ?? 0;
|
|
18
|
+
return { detail: `${count} bucket${count !== 1 ? "s" : ""} accessible` };
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
const RAILWAY_API = "https://backboard.railway.com/graphql/v2";
|
|
3
|
+
export async function checkRailway() {
|
|
4
|
+
const token = process.env.RAILWAY_API_TOKEN;
|
|
5
|
+
if (!token) {
|
|
6
|
+
return { name: "Railway API", status: "skipped", detail: "RAILWAY_API_TOKEN not set" };
|
|
7
|
+
}
|
|
8
|
+
return runCheck("Railway API", async () => {
|
|
9
|
+
const res = await fetch(RAILWAY_API, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: {
|
|
12
|
+
Authorization: `Bearer ${token}`,
|
|
13
|
+
"Content-Type": "application/json",
|
|
14
|
+
},
|
|
15
|
+
body: JSON.stringify({
|
|
16
|
+
query: `{ me { name email } }`,
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
throw new Error(`HTTP ${res.status}`);
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
if (data.errors)
|
|
23
|
+
throw new Error(data.errors[0].message);
|
|
24
|
+
return { detail: `logged in as ${data.data.me.name}` };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkRedis() {
|
|
3
|
+
const url = process.env.REDIS_URL;
|
|
4
|
+
if (!url) {
|
|
5
|
+
return { name: "Redis", status: "skipped", detail: "REDIS_URL not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("Redis", async () => {
|
|
8
|
+
const parsed = new URL(url);
|
|
9
|
+
const host = parsed.hostname;
|
|
10
|
+
const port = parseInt(parsed.port || "6379", 10);
|
|
11
|
+
const { createConnection } = await import("net");
|
|
12
|
+
await new Promise((resolve, reject) => {
|
|
13
|
+
const socket = createConnection({ host, port, timeout: 5000 }, () => {
|
|
14
|
+
// Send PING, expect +PONG
|
|
15
|
+
socket.write("PING\r\n");
|
|
16
|
+
socket.once("data", (data) => {
|
|
17
|
+
socket.destroy();
|
|
18
|
+
if (data.toString().includes("+PONG")) {
|
|
19
|
+
resolve();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
reject(new Error("unexpected response: " + data.toString().trim()));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
socket.on("error", reject);
|
|
27
|
+
socket.on("timeout", () => {
|
|
28
|
+
socket.destroy();
|
|
29
|
+
reject(new Error("connection timeout"));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
return { detail: `${host}:${port} responding` };
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { checkRailway } from "./railway.js";
|
|
2
|
+
import { checkDatabase } from "./database.js";
|
|
3
|
+
import { checkRedis } from "./redis.js";
|
|
4
|
+
import { checkVercel } from "./vercel.js";
|
|
5
|
+
import { checkR2 } from "./r2.js";
|
|
6
|
+
import { checkStripe } from "./stripe.js";
|
|
7
|
+
import { checkFly } from "./fly.js";
|
|
8
|
+
import { checkRender } from "./render.js";
|
|
9
|
+
import { checkSupabase } from "./supabase.js";
|
|
10
|
+
import { checkAWS } from "./aws.js";
|
|
11
|
+
import { checkMongoDB } from "./mongodb.js";
|
|
12
|
+
/**
|
|
13
|
+
* Registry mapping service names to their health check functions.
|
|
14
|
+
* New services just need to be added here.
|
|
15
|
+
*/
|
|
16
|
+
const registry = {
|
|
17
|
+
// Deploy providers
|
|
18
|
+
railway: checkRailway,
|
|
19
|
+
vercel: checkVercel,
|
|
20
|
+
fly: checkFly,
|
|
21
|
+
render: checkRender,
|
|
22
|
+
aws: checkAWS,
|
|
23
|
+
// Databases
|
|
24
|
+
database: checkDatabase,
|
|
25
|
+
mongodb: checkMongoDB,
|
|
26
|
+
redis: checkRedis,
|
|
27
|
+
supabase: checkSupabase,
|
|
28
|
+
// Storage & billing
|
|
29
|
+
r2: checkR2,
|
|
30
|
+
stripe: checkStripe,
|
|
31
|
+
};
|
|
32
|
+
export function getChecker(service) {
|
|
33
|
+
return registry[service] ?? null;
|
|
34
|
+
}
|
|
35
|
+
export function allCheckerNames() {
|
|
36
|
+
return Object.keys(registry);
|
|
37
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkRender() {
|
|
3
|
+
const token = process.env.RENDER_API_KEY;
|
|
4
|
+
if (!token) {
|
|
5
|
+
return { name: "Render", status: "skipped", detail: "RENDER_API_KEY not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("Render", async () => {
|
|
8
|
+
const res = await fetch("https://api.render.com/v1/services?limit=1", {
|
|
9
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new Error(`HTTP ${res.status}`);
|
|
13
|
+
const data = (await res.json());
|
|
14
|
+
const svc = data[0]?.service;
|
|
15
|
+
if (!svc)
|
|
16
|
+
return { detail: "no services found" };
|
|
17
|
+
return { detail: `${svc.name} — ${svc.suspended === "not_suspended" ? "running" : svc.suspended}` };
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkStripe() {
|
|
3
|
+
const key = process.env.STRIPE_SECRET_KEY;
|
|
4
|
+
if (!key) {
|
|
5
|
+
return { name: "Stripe", status: "skipped", detail: "STRIPE_SECRET_KEY not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("Stripe", async () => {
|
|
8
|
+
const res = await fetch("https://api.stripe.com/v1/balance", {
|
|
9
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new Error(`HTTP ${res.status}`);
|
|
13
|
+
const data = await res.json();
|
|
14
|
+
const mode = key.startsWith("sk_live") ? "live" : "test";
|
|
15
|
+
const available = data.available?.[0];
|
|
16
|
+
const detail = available
|
|
17
|
+
? `${mode} mode — balance: ${(available.amount / 100).toFixed(2)} ${available.currency.toUpperCase()}`
|
|
18
|
+
: `${mode} mode — API reachable`;
|
|
19
|
+
return { detail };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkSupabase() {
|
|
3
|
+
const url = process.env.SUPABASE_URL;
|
|
4
|
+
const key = process.env.SUPABASE_SERVICE_KEY ?? process.env.SUPABASE_ANON_KEY;
|
|
5
|
+
if (!url || !key) {
|
|
6
|
+
return {
|
|
7
|
+
name: "Supabase",
|
|
8
|
+
status: "skipped",
|
|
9
|
+
detail: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return runCheck("Supabase", async () => {
|
|
13
|
+
// Hit the REST health endpoint
|
|
14
|
+
const res = await fetch(`${url}/rest/v1/`, {
|
|
15
|
+
headers: {
|
|
16
|
+
apikey: key,
|
|
17
|
+
Authorization: `Bearer ${key}`,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok)
|
|
21
|
+
throw new Error(`HTTP ${res.status}`);
|
|
22
|
+
return { detail: "API reachable" };
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { runCheck } from "./checker.js";
|
|
2
|
+
export async function checkVercel() {
|
|
3
|
+
const token = process.env.VERCEL_TOKEN;
|
|
4
|
+
if (!token) {
|
|
5
|
+
return { name: "Vercel", status: "skipped", detail: "VERCEL_TOKEN not set" };
|
|
6
|
+
}
|
|
7
|
+
return runCheck("Vercel", async () => {
|
|
8
|
+
// Get latest deployment
|
|
9
|
+
const res = await fetch("https://api.vercel.com/v6/deployments?limit=1", {
|
|
10
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
11
|
+
});
|
|
12
|
+
if (!res.ok)
|
|
13
|
+
throw new Error(`HTTP ${res.status}`);
|
|
14
|
+
const data = await res.json();
|
|
15
|
+
const dep = data.deployments?.[0];
|
|
16
|
+
if (!dep)
|
|
17
|
+
return { detail: "no deployments found" };
|
|
18
|
+
const ago = timeSince(new Date(dep.created));
|
|
19
|
+
const state = dep.readyState ?? dep.state ?? "unknown";
|
|
20
|
+
return { detail: `last deploy: ${state} (${ago} ago)` };
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function timeSince(date) {
|
|
24
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
25
|
+
if (seconds < 60)
|
|
26
|
+
return `${seconds}s`;
|
|
27
|
+
const minutes = Math.floor(seconds / 60);
|
|
28
|
+
if (minutes < 60)
|
|
29
|
+
return `${minutes}m`;
|
|
30
|
+
const hours = Math.floor(minutes / 60);
|
|
31
|
+
if (hours < 24)
|
|
32
|
+
return `${hours}h`;
|
|
33
|
+
const days = Math.floor(hours / 24);
|
|
34
|
+
return `${days}d`;
|
|
35
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export const templates = {
|
|
2
|
+
saas: {
|
|
3
|
+
name: "SaaS Starter",
|
|
4
|
+
description: "Full SaaS stack — Vercel frontend, Railway backend, PostgreSQL, Redis, Stripe billing, R2 storage",
|
|
5
|
+
config: {
|
|
6
|
+
name: "my-saas",
|
|
7
|
+
services: {
|
|
8
|
+
vercel: true,
|
|
9
|
+
railway: true,
|
|
10
|
+
database: true,
|
|
11
|
+
redis: true,
|
|
12
|
+
stripe: true,
|
|
13
|
+
r2: true,
|
|
14
|
+
},
|
|
15
|
+
deploy: {
|
|
16
|
+
branch: "main",
|
|
17
|
+
providers: ["vercel", "railway"],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
api: {
|
|
22
|
+
name: "API Service",
|
|
23
|
+
description: "Backend API — Railway or Render, PostgreSQL, Redis cache",
|
|
24
|
+
config: {
|
|
25
|
+
name: "my-api",
|
|
26
|
+
services: {
|
|
27
|
+
railway: true,
|
|
28
|
+
database: true,
|
|
29
|
+
redis: true,
|
|
30
|
+
},
|
|
31
|
+
deploy: {
|
|
32
|
+
branch: "main",
|
|
33
|
+
providers: ["railway"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
fullstack: {
|
|
38
|
+
name: "Fullstack App",
|
|
39
|
+
description: "Vercel + Railway + Supabase with auth and storage built-in",
|
|
40
|
+
config: {
|
|
41
|
+
name: "my-app",
|
|
42
|
+
services: {
|
|
43
|
+
vercel: true,
|
|
44
|
+
railway: true,
|
|
45
|
+
supabase: true,
|
|
46
|
+
stripe: true,
|
|
47
|
+
},
|
|
48
|
+
deploy: {
|
|
49
|
+
branch: "main",
|
|
50
|
+
providers: ["vercel", "railway"],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
static: {
|
|
55
|
+
name: "Static / Jamstack",
|
|
56
|
+
description: "Vercel-only static site with optional Stripe for payments",
|
|
57
|
+
config: {
|
|
58
|
+
name: "my-site",
|
|
59
|
+
services: {
|
|
60
|
+
vercel: true,
|
|
61
|
+
stripe: false,
|
|
62
|
+
},
|
|
63
|
+
deploy: {
|
|
64
|
+
branch: "main",
|
|
65
|
+
providers: ["vercel"],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
fly: {
|
|
70
|
+
name: "Fly.io Stack",
|
|
71
|
+
description: "Fly.io deploy, PostgreSQL, Redis — edge-first architecture",
|
|
72
|
+
config: {
|
|
73
|
+
name: "my-fly-app",
|
|
74
|
+
services: {
|
|
75
|
+
fly: true,
|
|
76
|
+
database: true,
|
|
77
|
+
redis: true,
|
|
78
|
+
},
|
|
79
|
+
deploy: {
|
|
80
|
+
branch: "main",
|
|
81
|
+
providers: ["fly"],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
aws: {
|
|
86
|
+
name: "AWS Stack",
|
|
87
|
+
description: "AWS infrastructure with PostgreSQL and Redis",
|
|
88
|
+
config: {
|
|
89
|
+
name: "my-aws-app",
|
|
90
|
+
services: {
|
|
91
|
+
aws: true,
|
|
92
|
+
database: true,
|
|
93
|
+
redis: true,
|
|
94
|
+
r2: false,
|
|
95
|
+
},
|
|
96
|
+
deploy: {
|
|
97
|
+
branch: "main",
|
|
98
|
+
providers: ["aws"],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
export function listTemplates() {
|
|
104
|
+
return Object.keys(templates);
|
|
105
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prajwolkc/stk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One CLI to deploy, monitor, and debug your entire stack. Health checks, deploy watching, env sync, logs, and GitHub issues — all from one command.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "prajwolkc",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Harden43/stk"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cli",
|
|
14
|
+
"devops",
|
|
15
|
+
"deploy",
|
|
16
|
+
"monitoring",
|
|
17
|
+
"railway",
|
|
18
|
+
"vercel",
|
|
19
|
+
"fly",
|
|
20
|
+
"render",
|
|
21
|
+
"supabase",
|
|
22
|
+
"aws",
|
|
23
|
+
"stripe",
|
|
24
|
+
"health-check",
|
|
25
|
+
"infrastructure",
|
|
26
|
+
"developer-tools"
|
|
27
|
+
],
|
|
28
|
+
"bin": {
|
|
29
|
+
"stk": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"dev": "tsx src/index.ts",
|
|
42
|
+
"start": "node dist/index.js",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"chalk": "^5.4.1",
|
|
47
|
+
"commander": "^13.1.0",
|
|
48
|
+
"ora": "^8.2.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.13.0",
|
|
52
|
+
"tsx": "^4.19.0",
|
|
53
|
+
"typescript": "^5.7.0"
|
|
54
|
+
}
|
|
55
|
+
}
|