clawnify 0.1.1
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/dist/chunk-6UAOYVZ6.js +325 -0
- package/dist/chunk-6UAOYVZ6.js.map +1 -0
- package/dist/cli-MDRNA6W2.js +741 -0
- package/dist/cli-MDRNA6W2.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-JLE4IWRX.js +132 -0
- package/dist/mcp-JLE4IWRX.js.map +1 -0
- package/package.json +27 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/commands/deploy.ts","../src/commands/ls.ts","../src/commands/logs.ts","../src/commands/rm.ts","../src/commands/open.ts","../src/commands/whoami.ts","../src/commands/init.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { deployCommand } from \"./commands/deploy.js\";\nimport { lsCommand } from \"./commands/ls.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { rmCommand } from \"./commands/rm.js\";\nimport { openCommand } from \"./commands/open.js\";\nimport { whoamiCommand } from \"./commands/whoami.js\";\nimport { initCommand } from \"./commands/init.js\";\n\nexport function runCli() {\n const program = new Command();\n\n program\n .name(\"clawnify\")\n .description(\"Deploy apps to Clawnify\")\n .version(\"0.1.0\");\n\n program\n .command(\"init [dir]\")\n .description(\"Scaffold a new Clawnify app\")\n .action(initCommand);\n\n program\n .command(\"login\")\n .description(\"Authenticate with Clawnify\")\n .action(loginCommand);\n\n program\n .command(\"deploy [dir]\")\n .description(\"Deploy an app to Clawnify\")\n .option(\"--from <repo>\", \"Deploy from a GitHub repo (owner/repo)\")\n .option(\"--name <name>\", \"Override app name\")\n .action(deployCommand);\n\n program\n .command(\"ls\")\n .description(\"List deployed apps\")\n .action(lsCommand);\n\n program\n .command(\"logs <app-id>\")\n .description(\"Show build logs for an app\")\n .action(logsCommand);\n\n program\n .command(\"rm <app-id>\")\n .description(\"Delete an app\")\n .action(rmCommand);\n\n program\n .command(\"open <slug>\")\n .description(\"Open an app in the browser\")\n .action(openCommand);\n\n program\n .command(\"whoami\")\n .description(\"Show current user info\")\n .action(whoamiCommand);\n\n program.parse();\n}\n","import crypto from \"node:crypto\";\nimport http from \"node:http\";\nimport readline from \"node:readline\";\nimport open from \"open\";\nimport {\n saveAuth,\n generatePkce,\n OAUTH_AUTHORIZE_URL,\n OAUTH_TOKEN_URL,\n OAUTH_CLIENT_ID,\n} from \"../lib/auth.js\";\nimport { api } from \"../lib/api.js\";\n\nfunction findPort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = http.createServer();\n server.listen(0, () => {\n const addr = server.address();\n if (addr && typeof addr === \"object\") {\n const port = addr.port;\n server.close(() => resolve(port));\n } else {\n reject(new Error(\"Could not find available port\"));\n }\n });\n });\n}\n\nexport async function loginCommand(): Promise<void> {\n const { codeVerifier: verifier, codeChallenge: challenge } = generatePkce();\n const state = crypto.randomBytes(16).toString(\"hex\");\n const port = 19823;\n const redirectUri = `http://localhost:${port}/callback`;\n\n const authUrl = new URL(OAUTH_AUTHORIZE_URL);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"client_id\", OAUTH_CLIENT_ID);\n authUrl.searchParams.set(\"redirect_uri\", redirectUri);\n authUrl.searchParams.set(\"code_challenge\", challenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"scope\", \"openid email\");\n authUrl.searchParams.set(\"state\", state);\n\n const result = await new Promise<{\n access_token: string;\n refresh_token: string;\n expires_in: number;\n }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n server.close();\n reject(new Error(\"Timed out waiting for authorization.\"));\n }, 5 * 60 * 1000);\n\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://localhost:${port}`);\n if (url.pathname !== \"/callback\") {\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n if (error) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(\n \"<html><body><h2>Authorization denied.</h2><p>You can close this window.</p></body></html>\"\n );\n clearTimeout(timeout);\n server.close();\n reject(new Error(`Authorization denied: ${error}`));\n return;\n }\n\n if (!code || returnedState !== state) {\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(\n \"<html><body><h2>Invalid callback.</h2></body></html>\"\n );\n return;\n }\n\n // Exchange code for tokens\n try {\n const tokenRes = await fetch(OAUTH_TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n client_id: OAUTH_CLIENT_ID,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n });\n\n if (!tokenRes.ok) {\n const err = await tokenRes.text();\n throw new Error(`Token exchange failed: ${err}`);\n }\n\n const tokens = (await tokenRes.json()) as {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n token_type: string;\n };\n\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(\n \"<html><body><h2>Logged in!</h2><p>You can close this window and return to the terminal.</p></body></html>\"\n );\n\n clearTimeout(timeout);\n server.close();\n resolve(tokens);\n } catch (err) {\n res.writeHead(500, { \"Content-Type\": \"text/html\" });\n res.end(\n \"<html><body><h2>Login failed.</h2></body></html>\"\n );\n clearTimeout(timeout);\n server.close();\n reject(err);\n }\n });\n\n server.listen(port, () => {\n console.log(\"Opening browser to authorize...\");\n open(authUrl.toString());\n console.log(\"Waiting for authorization...\");\n });\n });\n\n // Decode JWT to get email\n let email = \"unknown\";\n try {\n const payload = JSON.parse(\n Buffer.from(result.access_token.split(\".\")[1], \"base64\").toString()\n );\n email = payload.email || \"unknown\";\n } catch {\n // ignore decode errors\n }\n\n const expiresAt = Math.floor(Date.now() / 1000) + result.expires_in;\n\n // Save tokens first (needed for API calls)\n saveAuth({\n access_token: result.access_token,\n refresh_token: result.refresh_token,\n expires_at: expiresAt,\n });\n\n // Fetch user's orgs and prompt to select\n let orgId: string | undefined;\n try {\n const orgsRes = await api.get(\"/v1/orgs\");\n if (orgsRes.ok) {\n const { orgs } = (await orgsRes.json()) as {\n orgs: Array<{ id: string; name: string; slug: string; role: string }>;\n };\n\n if (orgs.length === 1) {\n orgId = orgs[0].id;\n console.log(`Organization: ${orgs[0].name}`);\n } else if (orgs.length > 1) {\n console.log(\"\\nSelect an organization:\\n\");\n orgs.forEach((org, i) => {\n console.log(` ${i + 1}) ${org.name} (${org.role})`);\n });\n console.log();\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const choice = await new Promise<string>((resolve) => {\n rl.question(\"Enter number: \", (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n\n const index = parseInt(choice, 10) - 1;\n if (index >= 0 && index < orgs.length) {\n orgId = orgs[index].id;\n console.log(`Selected: ${orgs[index].name}`);\n } else {\n orgId = orgs[0].id;\n console.log(`Defaulting to: ${orgs[0].name}`);\n }\n }\n }\n } catch {\n // If org fetch fails, continue without org_id\n }\n\n // Save again with org_id\n saveAuth({\n access_token: result.access_token,\n refresh_token: result.refresh_token,\n expires_at: expiresAt,\n org_id: orgId,\n });\n\n console.log(`Logged in as ${email}`);\n}\n","import path from \"node:path\";\nimport { api } from \"../lib/api.js\";\nimport { createArchive } from \"../lib/archive.js\";\nimport { readManifest, saveProject } from \"../lib/config.js\";\nimport { pollUntilReady } from \"../lib/poll.js\";\nexport async function deployCommand(\n dir?: string,\n options?: { from?: string; name?: string }\n): Promise<void> {\n let appId: string;\n let slug: string;\n let appToken: string;\n\n if (options?.from) {\n const repo = options.from.replace(/^github:/, \"\");\n console.log(`Deploying from ${repo}...`);\n\n const res = await api.post(\"/v1/apps/deploy\", { repo, branch: \"main\" });\n if (!res.ok) {\n const err = (await res.json()) as { error?: string };\n console.error(`Deploy failed: ${err.error || res.statusText}`);\n process.exit(1);\n }\n\n const data = (await res.json()) as {\n app_id: string;\n slug: string;\n app_token: string;\n };\n appId = data.app_id;\n slug = data.slug;\n appToken = data.app_token;\n } else {\n const resolvedDir = dir ? path.resolve(dir) : process.cwd();\n const manifest = readManifest(resolvedDir);\n const name = options?.name || manifest?.name || path.basename(resolvedDir);\n const framework = manifest?.app?.framework || \"vite-preact\";\n\n console.log(`Deploying ${name}...`);\n\n const archive = createArchive(resolvedDir);\n const formData = new FormData();\n formData.append(\"source\", new Blob([archive]), \"source.tar.gz\");\n formData.append(\"name\", name);\n formData.append(\"framework\", framework);\n\n const res = await api.postForm(\"/v1/apps/deploy\", formData);\n if (!res.ok) {\n const err = (await res.json()) as { error?: string };\n console.error(`Deploy failed: ${err.error || res.statusText}`);\n process.exit(1);\n }\n\n const data = (await res.json()) as {\n app_id: string;\n slug: string;\n app_token: string;\n };\n appId = data.app_id;\n slug = data.slug;\n appToken = data.app_token;\n\n saveProject(resolvedDir, {\n app_id: appId,\n slug,\n app_token: appToken,\n });\n }\n\n const result = await pollUntilReady(appId);\n\n if (result.status === \"error\") {\n console.error(`\\n Deploy failed: ${result.error || \"Unknown error\"}\\n`);\n process.exit(1);\n }\n\n const url = `https://${slug}.apps.clawnify.com`;\n console.log(`\\n ✓ Deployed! ${url}\\n`);\n console.log(` Open: clawnify open ${slug}\\n`);\n}\n","import { api } from \"../lib/api.js\";\n\ninterface App {\n id: string;\n name: string;\n slug: string;\n status: string;\n}\n\nexport async function lsCommand(): Promise<void> {\n const res = await api.get(\"/v1/apps\");\n if (!res.ok) {\n console.error(\"Failed to list apps.\");\n process.exit(1);\n }\n\n const apps = (await res.json()) as App[];\n\n if (apps.length === 0) {\n console.log(\"No apps deployed yet.\");\n return;\n }\n\n const nameWidth = Math.max(4, ...apps.map((a) => a.name.length));\n const slugWidth = Math.max(4, ...apps.map((a) => a.slug.length));\n\n console.log(\n `${\"Name\".padEnd(nameWidth)} ${\"Slug\".padEnd(slugWidth)} ${\"Status\".padEnd(8)} URL`\n );\n console.log(\n `${\"─\".repeat(nameWidth)} ${\"─\".repeat(slugWidth)} ${\"─\".repeat(8)} ${\"─\".repeat(30)}`\n );\n\n for (const app of apps) {\n const url = `https://${app.slug}.apps.clawnify.com`;\n console.log(\n `${app.name.padEnd(nameWidth)} ${app.slug.padEnd(slugWidth)} ${app.status.padEnd(8)} ${url}`\n );\n }\n}\n","import { api } from \"../lib/api.js\";\n\ninterface AppLogs {\n status: string;\n status_message?: string;\n logs?: string;\n}\n\nexport async function logsCommand(appId: string): Promise<void> {\n const res = await api.get(`/v1/apps/${appId}/logs`);\n if (!res.ok) {\n console.error(\"Failed to fetch logs.\");\n process.exit(1);\n }\n\n const data = (await res.json()) as AppLogs;\n\n console.log(`Status: ${data.status}`);\n if (data.status_message) {\n console.log(`Message: ${data.status_message}`);\n }\n if (data.logs) {\n console.log(\"\\n--- Build Logs ---\\n\");\n console.log(data.logs);\n }\n}\n","import readline from \"node:readline\";\nimport { api } from \"../lib/api.js\";\n\ninterface App {\n name: string;\n}\n\nexport async function rmCommand(appId: string): Promise<void> {\n const res = await api.get(`/v1/apps/${appId}`);\n if (!res.ok) {\n console.error(\"App not found.\");\n process.exit(1);\n }\n\n const app = (await res.json()) as App;\n\n const confirmed = await new Promise<boolean>((resolve) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n rl.question(`Delete ${app.name}? (y/N) `, (answer) => {\n rl.close();\n resolve(answer.toLowerCase() === \"y\");\n });\n });\n\n if (!confirmed) {\n console.log(\"Cancelled.\");\n return;\n }\n\n const delRes = await api.del(`/v1/apps/${appId}`);\n if (!delRes.ok) {\n console.error(\"Failed to delete app.\");\n process.exit(1);\n }\n\n console.log(`Deleted ${app.name}`);\n}\n","import open from \"open\";\nimport { readProject } from \"../lib/config.js\";\nimport { api } from \"../lib/api.js\";\n\ninterface App {\n slug: string;\n app_token?: string;\n}\n\nexport async function openCommand(slug: string): Promise<void> {\n let appToken: string | undefined;\n\n const project = readProject();\n if (project?.slug === slug && project.app_token) {\n appToken = project.app_token;\n }\n\n if (!appToken) {\n const res = await api.get(\"/v1/apps\");\n if (res.ok) {\n const apps = (await res.json()) as App[];\n const app = apps.find((a) => a.slug === slug);\n appToken = app?.app_token;\n }\n }\n\n let url = `https://${slug}.apps.clawnify.com`;\n if (appToken) {\n url += `?token=${appToken}`;\n }\n\n await open(url);\n console.log(\"Opened in browser\");\n}\n","import { loadAuth, getEmailFromToken } from \"../lib/auth.js\";\n\nexport async function whoamiCommand(): Promise<void> {\n const auth = loadAuth();\n\n if (!auth) {\n console.log(\"Not logged in\");\n return;\n }\n\n const email = getEmailFromToken(auth.access_token) ?? \"unknown\";\n console.log(`Email: ${email}`);\n if (auth.org_id) {\n console.log(`Org: ${auth.org_id}`);\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nfunction ask(question: string): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nfunction slugify(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 30);\n}\n\nconst TEMPLATES = {\n blank: \"Empty app with a single API route and page\",\n crud: \"CRUD app with a database table, list, and form\",\n};\n\nexport async function initCommand(dir?: string): Promise<void> {\n const name = (await ask(\"App name: \")) || \"my-app\";\n const slug = slugify(name);\n const targetDir = dir ? path.resolve(dir) : path.resolve(slug);\n\n console.log();\n console.log(\" Templates:\");\n console.log(\" 1) blank — Empty app with a single API route and page\");\n console.log(\" 2) crud — CRUD app with database, list, and form\");\n console.log();\n const templateChoice = (await ask(\"Template [1]: \")) || \"1\";\n const template = templateChoice === \"2\" ? \"crud\" : \"blank\";\n\n if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {\n console.error(`Directory ${targetDir} is not empty.`);\n process.exit(1);\n }\n\n fs.mkdirSync(path.join(targetDir, \"src/server\"), { recursive: true });\n fs.mkdirSync(path.join(targetDir, \"src/client\"), { recursive: true });\n\n // clawnify.json\n write(targetDir, \"clawnify.json\", JSON.stringify({\n $schema: \"https://app.clawnify.com/schema/v1/clawnify.json\",\n version: 1,\n name,\n description: \"\",\n app: { framework: \"preact+hono\", database: true, storage: false },\n }, null, 2));\n\n // package.json\n write(targetDir, \"package.json\", JSON.stringify({\n name: slug,\n private: true,\n type: \"module\",\n scripts: {\n dev: \"wrangler dev\",\n build: \"vite build\",\n },\n dependencies: {\n hono: \"^4.0.0\",\n preact: \"^10.0.0\",\n },\n devDependencies: {\n \"@preact/preset-vite\": \"^2.0.0\",\n \"vite\": \"^6.0.0\",\n \"wrangler\": \"^4.0.0\",\n },\n pnpm: { onlyBuiltDependencies: [\"esbuild\"] },\n }, null, 2));\n\n // tsconfig.json\n write(targetDir, \"tsconfig.json\", JSON.stringify({\n compilerOptions: {\n target: \"ESNext\",\n module: \"ESNext\",\n moduleResolution: \"Bundler\",\n strict: true,\n lib: [\"ESNext\", \"DOM\", \"DOM.Iterable\"],\n jsx: \"react-jsx\",\n jsxImportSource: \"preact\",\n noEmit: true,\n skipLibCheck: true,\n resolveJsonModule: true,\n isolatedModules: true,\n },\n include: [\"src/**/*.ts\", \"src/**/*.tsx\"],\n exclude: [\"node_modules\", \"dist\"],\n }, null, 2));\n\n // vite.config.ts\n write(targetDir, \"vite.config.ts\", `import { defineConfig } from \"vite\";\nimport preact from \"@preact/preset-vite\";\n\nexport default defineConfig({\n plugins: [preact()],\n build: {\n outDir: \"dist\",\n emptyOutDir: true,\n },\n server: {\n proxy: {\n \"/api\": {\n target: \"http://localhost:8787\",\n changeOrigin: true,\n },\n },\n },\n});\n`);\n\n // wrangler.toml (local dev — skipped on deploy)\n write(targetDir, \"wrangler.toml\", `name = \"${slug}\"\nmain = \"src/server/index.ts\"\ncompatibility_date = \"2025-04-01\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"${slug}-db\"\ndatabase_id = \"local\"\n\n[assets]\ndirectory = \"./dist\"\nbinding = \"ASSETS\"\nnot_found_handling = \"single-page-application\"\n`);\n\n // index.html\n write(targetDir, \"index.html\", `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${name}</title>\n</head>\n<body>\n <div id=\"app\"></div>\n <script type=\"module\" src=\"/src/client/main.tsx\"></script>\n</body>\n</html>\n`);\n\n // .gitignore\n write(targetDir, \".gitignore\", `node_modules\ndist\n.wrangler\n.clawnify\n`);\n\n // src/server/db.ts — D1-native\n write(targetDir, \"src/server/db.ts\", `let _db: D1Database;\n\nexport function initDB(db: D1Database) {\n _db = db;\n}\n\nexport async function query<T = Record<string, unknown>>(\n sql: string,\n params: unknown[] = [],\n): Promise<T[]> {\n const stmt = _db.prepare(sql).bind(...params);\n const { results } = await stmt.all<T>();\n return results ?? [];\n}\n\nexport async function get<T = Record<string, unknown>>(\n sql: string,\n params: unknown[] = [],\n): Promise<T | undefined> {\n const stmt = _db.prepare(sql).bind(...params);\n const row = await stmt.first<T>();\n return row ?? undefined;\n}\n\nexport async function run(\n sql: string,\n params: unknown[] = [],\n): Promise<{ changes: number; lastInsertRowid: number }> {\n const stmt = _db.prepare(sql).bind(...params);\n const result = await stmt.run();\n return {\n changes: result.meta?.changes ?? 0,\n lastInsertRowid: Number(result.meta?.last_row_id ?? 0),\n };\n}\n`);\n\n // src/server/index.ts\n write(targetDir, \"src/server/index.ts\", `import { Hono } from \"hono\";\nimport { initDB } from \"./db\";\nimport api from \"./routes\";\n\ntype Env = { Bindings: { DB: D1Database } };\n\nconst app = new Hono<Env>();\n\napp.use(\"*\", async (c, next) => {\n initDB(c.env.DB);\n await next();\n});\n\napp.route(\"/\", api);\n\nexport default app;\n`);\n\n // src/client/main.tsx\n write(targetDir, \"src/client/main.tsx\", `import { render } from \"preact\";\nimport { App } from \"./app\";\n\nrender(<App />, document.getElementById(\"app\")!);\n`);\n\n if (template === \"crud\") {\n writeCrudTemplate(targetDir, name);\n } else {\n writeBlankTemplate(targetDir, name);\n }\n\n console.log();\n console.log(` Created ${name} in ${path.relative(process.cwd(), targetDir)}/`);\n console.log();\n console.log(\" Get started:\");\n console.log(` cd ${path.relative(process.cwd(), targetDir)}`);\n console.log(\" pnpm install\");\n console.log(\" pnpm dev # local dev at http://localhost:8787\");\n console.log(\" clawnify deploy # deploy to production\");\n console.log();\n}\n\nfunction writeBlankTemplate(dir: string, name: string) {\n write(dir, \"src/server/schema.sql\", `CREATE TABLE IF NOT EXISTS items (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n`);\n\n write(dir, \"src/server/routes.ts\", `import { Hono } from \"hono\";\nimport { query } from \"./db\";\n\nconst api = new Hono();\n\napi.get(\"/api/items\", async (c) => {\n const items = await query(\"SELECT * FROM items ORDER BY created_at DESC\");\n return c.json(items);\n});\n\nexport default api;\n`);\n\n write(dir, \"src/client/app.tsx\", `import { useState, useEffect } from \"preact/hooks\";\n\nexport function App() {\n const [items, setItems] = useState<any[]>([]);\n\n useEffect(() => {\n fetch(\"/api/items\").then((r) => r.json()).then(setItems);\n }, []);\n\n return (\n <div style={{ maxWidth: 480, margin: \"40px auto\", fontFamily: \"system-ui\" }}>\n <h1>${name}</h1>\n <ul>\n {items.map((item: any) => (\n <li key={item.id}>{item.name}</li>\n ))}\n </ul>\n {items.length === 0 && <p style={{ color: \"#999\" }}>No items yet.</p>}\n </div>\n );\n}\n`);\n}\n\nfunction writeCrudTemplate(dir: string, name: string) {\n write(dir, \"src/server/schema.sql\", `CREATE TABLE IF NOT EXISTS items (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n title TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n`);\n\n write(dir, \"src/server/routes.ts\", `import { Hono } from \"hono\";\nimport { query, run } from \"./db\";\n\nconst api = new Hono();\n\napi.get(\"/api/items\", async (c) => {\n const items = await query(\"SELECT * FROM items ORDER BY created_at DESC\");\n return c.json(items);\n});\n\napi.post(\"/api/items\", async (c) => {\n const { title } = await c.req.json<{ title: string }>();\n if (!title?.trim()) return c.json({ error: \"Title required\" }, 400);\n const result = await run(\"INSERT INTO items (title) VALUES (?)\", [title.trim()]);\n return c.json({ id: result.lastInsertRowid, title: title.trim(), status: \"active\" }, 201);\n});\n\napi.patch(\"/api/items/:id\", async (c) => {\n const id = c.req.param(\"id\");\n const body = await c.req.json<{ title?: string; status?: string }>();\n const sets: string[] = [];\n const params: unknown[] = [];\n if (body.title !== undefined) { sets.push(\"title = ?\"); params.push(body.title); }\n if (body.status !== undefined) { sets.push(\"status = ?\"); params.push(body.status); }\n if (sets.length === 0) return c.json({ error: \"Nothing to update\" }, 400);\n params.push(id);\n await run(\\`UPDATE items SET \\${sets.join(\", \")} WHERE id = ?\\`, params);\n return c.json({ ok: true });\n});\n\napi.delete(\"/api/items/:id\", async (c) => {\n const id = c.req.param(\"id\");\n await run(\"DELETE FROM items WHERE id = ?\", [id]);\n return c.json({ ok: true });\n});\n\nexport default api;\n`);\n\n write(dir, \"src/client/app.tsx\", `import { useState, useEffect } from \"preact/hooks\";\n\ninterface Item {\n id: number;\n title: string;\n status: string;\n created_at: string;\n}\n\nexport function App() {\n const [items, setItems] = useState<Item[]>([]);\n const [title, setTitle] = useState(\"\");\n\n async function load() {\n const res = await fetch(\"/api/items\");\n setItems(await res.json());\n }\n\n useEffect(() => { load(); }, []);\n\n async function addItem(e: Event) {\n e.preventDefault();\n if (!title.trim()) return;\n await fetch(\"/api/items\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ title }),\n });\n setTitle(\"\");\n load();\n }\n\n async function toggleStatus(item: Item) {\n const next = item.status === \"active\" ? \"done\" : \"active\";\n await fetch(\\`/api/items/\\${item.id}\\`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ status: next }),\n });\n load();\n }\n\n async function deleteItem(id: number) {\n await fetch(\\`/api/items/\\${id}\\`, { method: \"DELETE\" });\n load();\n }\n\n return (\n <div style={{ maxWidth: 480, margin: \"40px auto\", fontFamily: \"system-ui\" }}>\n <h1>${name}</h1>\n <form onSubmit={addItem} style={{ display: \"flex\", gap: 8, marginBottom: 16 }}>\n <input\n value={title}\n onInput={(e) => setTitle((e.target as HTMLInputElement).value)}\n placeholder=\"Add an item...\"\n style={{ flex: 1, padding: \"8px 12px\", borderRadius: 6, border: \"1px solid #ddd\" }}\n />\n <button type=\"submit\" style={{ padding: \"8px 16px\", borderRadius: 6, background: \"#111\", color: \"#fff\", border: \"none\", cursor: \"pointer\" }}>\n Add\n </button>\n </form>\n <ul style={{ listStyle: \"none\", padding: 0 }}>\n {items.map((item) => (\n <li key={item.id} style={{ display: \"flex\", alignItems: \"center\", gap: 8, padding: \"8px 0\", borderBottom: \"1px solid #eee\" }}>\n <input type=\"checkbox\" checked={item.status === \"done\"} onChange={() => toggleStatus(item)} />\n <span style={{ flex: 1, textDecoration: item.status === \"done\" ? \"line-through\" : \"none\", color: item.status === \"done\" ? \"#999\" : \"#111\" }}>\n {item.title}\n </span>\n <button onClick={() => deleteItem(item.id)} style={{ background: \"none\", border: \"none\", color: \"#999\", cursor: \"pointer\" }}>\n ×\n </button>\n </li>\n ))}\n </ul>\n {items.length === 0 && <p style={{ color: \"#999\" }}>No items yet. Add one above.</p>}\n </div>\n );\n}\n`);\n}\n\nfunction write(dir: string, file: string, content: string) {\n const fullPath = path.join(dir, file);\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n fs.writeFileSync(fullPath, content);\n}\n"],"mappings":"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","names":["readline","readline","open","open","path","readline","path"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
if (process.argv.includes("--mcp")) {
|
|
5
|
+
const { startMcpServer } = await import("./mcp-JLE4IWRX.js");
|
|
6
|
+
await startMcpServer();
|
|
7
|
+
} else {
|
|
8
|
+
const { runCli } = await import("./cli-MDRNA6W2.js");
|
|
9
|
+
runCli();
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["if (process.argv.includes(\"--mcp\")) {\n const { startMcpServer } = await import(\"./mcp.js\");\n await startMcpServer();\n} else {\n const { runCli } = await import(\"./cli.js\");\n runCli();\n}\n"],"mappings":";;;AAAA,IAAI,QAAQ,KAAK,SAAS,OAAO,GAAG;AAClC,QAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAU;AAClD,QAAM,eAAe;AACvB,OAAO;AACL,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,mBAAU;AAC1C,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
api,
|
|
4
|
+
createArchive,
|
|
5
|
+
getValidToken,
|
|
6
|
+
pollUntilReady,
|
|
7
|
+
readManifest,
|
|
8
|
+
saveProject
|
|
9
|
+
} from "./chunk-6UAOYVZ6.js";
|
|
10
|
+
|
|
11
|
+
// src/mcp.ts
|
|
12
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import path from "path";
|
|
16
|
+
async function startMcpServer() {
|
|
17
|
+
const server = new McpServer({ name: "clawnify", version: "0.1.0" });
|
|
18
|
+
server.tool(
|
|
19
|
+
"deploy",
|
|
20
|
+
"Deploy an app to Clawnify",
|
|
21
|
+
{
|
|
22
|
+
directory: z.string().optional().describe("Directory to deploy (default: cwd)"),
|
|
23
|
+
name: z.string().optional().describe("App name override"),
|
|
24
|
+
repo: z.string().optional().describe("GitHub repo (owner/repo) to deploy")
|
|
25
|
+
},
|
|
26
|
+
async (params) => {
|
|
27
|
+
const token = await getValidToken();
|
|
28
|
+
if (!token) {
|
|
29
|
+
return { content: [{ type: "text", text: "Not logged in. Run `clawnify login` first." }] };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
let appId;
|
|
33
|
+
let slug;
|
|
34
|
+
if (params.repo) {
|
|
35
|
+
const repo = params.repo.replace(/^github:/, "");
|
|
36
|
+
const res = await api.post("/v1/apps/deploy", { repo, branch: "main" });
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
const err = await res.json();
|
|
39
|
+
return { content: [{ type: "text", text: `Deploy failed: ${err.error || res.statusText}` }] };
|
|
40
|
+
}
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
appId = data.app_id;
|
|
43
|
+
slug = data.slug;
|
|
44
|
+
} else {
|
|
45
|
+
const dir = params.directory ? path.resolve(params.directory) : process.cwd();
|
|
46
|
+
const manifest = readManifest(dir);
|
|
47
|
+
const name = params.name || manifest?.name || path.basename(dir);
|
|
48
|
+
const framework = manifest?.app?.framework || "vite-preact";
|
|
49
|
+
const archive = createArchive(dir);
|
|
50
|
+
const formData = new FormData();
|
|
51
|
+
formData.append("source", new Blob([archive]), "source.tar.gz");
|
|
52
|
+
formData.append("name", name);
|
|
53
|
+
formData.append("framework", framework);
|
|
54
|
+
const res = await api.postForm("/v1/apps/deploy", formData);
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const err = await res.json();
|
|
57
|
+
return { content: [{ type: "text", text: `Deploy failed: ${err.error || res.statusText}` }] };
|
|
58
|
+
}
|
|
59
|
+
const data = await res.json();
|
|
60
|
+
appId = data.app_id;
|
|
61
|
+
slug = data.slug;
|
|
62
|
+
saveProject(dir, { app_id: appId, slug, app_token: data.app_token });
|
|
63
|
+
}
|
|
64
|
+
const result = await pollUntilReady(appId);
|
|
65
|
+
if (result.status === "error") {
|
|
66
|
+
return { content: [{ type: "text", text: `Deploy failed: ${result.error || "Unknown error"}` }] };
|
|
67
|
+
}
|
|
68
|
+
const url = `https://${slug}.apps.clawnify.com`;
|
|
69
|
+
return { content: [{ type: "text", text: `Deployed! ${url}` }] };
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }] };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
server.tool("list_apps", "List deployed apps", {}, async () => {
|
|
76
|
+
const token = await getValidToken();
|
|
77
|
+
if (!token) {
|
|
78
|
+
return { content: [{ type: "text", text: "Not logged in. Run `clawnify login` first." }] };
|
|
79
|
+
}
|
|
80
|
+
const res = await api.get("/v1/apps");
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
return { content: [{ type: "text", text: "Failed to list apps." }] };
|
|
83
|
+
}
|
|
84
|
+
const apps = await res.json();
|
|
85
|
+
if (apps.length === 0) {
|
|
86
|
+
return { content: [{ type: "text", text: "No apps deployed." }] };
|
|
87
|
+
}
|
|
88
|
+
const lines = apps.map(
|
|
89
|
+
(a) => `${a.name} (${a.slug}) \u2014 ${a.status} \u2014 https://${a.slug}.apps.clawnify.com`
|
|
90
|
+
);
|
|
91
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
92
|
+
});
|
|
93
|
+
server.tool(
|
|
94
|
+
"get_app",
|
|
95
|
+
"Get app status",
|
|
96
|
+
{ app_id: z.string().describe("App ID") },
|
|
97
|
+
async (params) => {
|
|
98
|
+
const token = await getValidToken();
|
|
99
|
+
if (!token) {
|
|
100
|
+
return { content: [{ type: "text", text: "Not logged in. Run `clawnify login` first." }] };
|
|
101
|
+
}
|
|
102
|
+
const res = await api.get(`/v1/apps/${params.app_id}`);
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
return { content: [{ type: "text", text: "App not found." }] };
|
|
105
|
+
}
|
|
106
|
+
const app = await res.json();
|
|
107
|
+
return { content: [{ type: "text", text: JSON.stringify(app, null, 2) }] };
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
server.tool(
|
|
111
|
+
"delete_app",
|
|
112
|
+
"Delete an app",
|
|
113
|
+
{ app_id: z.string().describe("App ID") },
|
|
114
|
+
async (params) => {
|
|
115
|
+
const token = await getValidToken();
|
|
116
|
+
if (!token) {
|
|
117
|
+
return { content: [{ type: "text", text: "Not logged in. Run `clawnify login` first." }] };
|
|
118
|
+
}
|
|
119
|
+
const res = await api.del(`/v1/apps/${params.app_id}`);
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
return { content: [{ type: "text", text: "Failed to delete app." }] };
|
|
122
|
+
}
|
|
123
|
+
return { content: [{ type: "text", text: "App deleted." }] };
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
const transport = new StdioServerTransport();
|
|
127
|
+
await server.connect(transport);
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
startMcpServer
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=mcp-JLE4IWRX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport path from \"node:path\";\nimport { getValidToken } from \"./lib/auth.js\";\nimport { api } from \"./lib/api.js\";\nimport { createArchive } from \"./lib/archive.js\";\nimport { readManifest, saveProject } from \"./lib/config.js\";\nimport { pollUntilReady } from \"./lib/poll.js\";\n\nexport async function startMcpServer() {\n const server = new McpServer({ name: \"clawnify\", version: \"0.1.0\" });\n\n server.tool(\n \"deploy\",\n \"Deploy an app to Clawnify\",\n {\n directory: z.string().optional().describe(\"Directory to deploy (default: cwd)\"),\n name: z.string().optional().describe(\"App name override\"),\n repo: z.string().optional().describe(\"GitHub repo (owner/repo) to deploy\"),\n },\n async (params) => {\n const token = await getValidToken();\n if (!token) {\n return { content: [{ type: \"text\" as const, text: \"Not logged in. Run `clawnify login` first.\" }] };\n }\n\n try {\n let appId: string;\n let slug: string;\n\n if (params.repo) {\n const repo = params.repo.replace(/^github:/, \"\");\n const res = await api.post(\"/v1/apps/deploy\", { repo, branch: \"main\" });\n if (!res.ok) {\n const err = (await res.json()) as { error?: string };\n return { content: [{ type: \"text\" as const, text: `Deploy failed: ${err.error || res.statusText}` }] };\n }\n const data = (await res.json()) as { app_id: string; slug: string; app_token: string };\n appId = data.app_id;\n slug = data.slug;\n } else {\n const dir = params.directory ? path.resolve(params.directory) : process.cwd();\n const manifest = readManifest(dir);\n const name = params.name || manifest?.name || path.basename(dir);\n const framework = manifest?.app?.framework || \"vite-preact\";\n\n const archive = createArchive(dir);\n const formData = new FormData();\n formData.append(\"source\", new Blob([archive]), \"source.tar.gz\");\n formData.append(\"name\", name);\n formData.append(\"framework\", framework);\n\n const res = await api.postForm(\"/v1/apps/deploy\", formData);\n if (!res.ok) {\n const err = (await res.json()) as { error?: string };\n return { content: [{ type: \"text\" as const, text: `Deploy failed: ${err.error || res.statusText}` }] };\n }\n const data = (await res.json()) as { app_id: string; slug: string; app_token: string };\n appId = data.app_id;\n slug = data.slug;\n\n saveProject(dir, { app_id: appId, slug, app_token: data.app_token });\n }\n\n const result = await pollUntilReady(appId);\n if (result.status === \"error\") {\n return { content: [{ type: \"text\" as const, text: `Deploy failed: ${result.error || \"Unknown error\"}` }] };\n }\n\n const url = `https://${slug}.apps.clawnify.com`;\n return { content: [{ type: \"text\" as const, text: `Deployed! ${url}` }] };\n } catch (err) {\n return { content: [{ type: \"text\" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}` }] };\n }\n }\n );\n\n server.tool(\"list_apps\", \"List deployed apps\", {}, async () => {\n const token = await getValidToken();\n if (!token) {\n return { content: [{ type: \"text\" as const, text: \"Not logged in. Run `clawnify login` first.\" }] };\n }\n\n const res = await api.get(\"/v1/apps\");\n if (!res.ok) {\n return { content: [{ type: \"text\" as const, text: \"Failed to list apps.\" }] };\n }\n\n const apps = (await res.json()) as Array<{ id: string; name: string; slug: string; status: string }>;\n if (apps.length === 0) {\n return { content: [{ type: \"text\" as const, text: \"No apps deployed.\" }] };\n }\n\n const lines = apps.map(\n (a) => `${a.name} (${a.slug}) — ${a.status} — https://${a.slug}.apps.clawnify.com`\n );\n return { content: [{ type: \"text\" as const, text: lines.join(\"\\n\") }] };\n });\n\n server.tool(\n \"get_app\",\n \"Get app status\",\n { app_id: z.string().describe(\"App ID\") },\n async (params) => {\n const token = await getValidToken();\n if (!token) {\n return { content: [{ type: \"text\" as const, text: \"Not logged in. Run `clawnify login` first.\" }] };\n }\n\n const res = await api.get(`/v1/apps/${params.app_id}`);\n if (!res.ok) {\n return { content: [{ type: \"text\" as const, text: \"App not found.\" }] };\n }\n\n const app = (await res.json()) as Record<string, unknown>;\n return { content: [{ type: \"text\" as const, text: JSON.stringify(app, null, 2) }] };\n }\n );\n\n server.tool(\n \"delete_app\",\n \"Delete an app\",\n { app_id: z.string().describe(\"App ID\") },\n async (params) => {\n const token = await getValidToken();\n if (!token) {\n return { content: [{ type: \"text\" as const, text: \"Not logged in. Run `clawnify login` first.\" }] };\n }\n\n const res = await api.del(`/v1/apps/${params.app_id}`);\n if (!res.ok) {\n return { content: [{ type: \"text\" as const, text: \"Failed to delete app.\" }] };\n }\n\n return { content: [{ type: \"text\" as const, text: \"App deleted.\" }] };\n }\n );\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clawnify",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Deploy apps to Clawnify from the terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clawnify": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
18
|
+
"commander": "^13.0.0",
|
|
19
|
+
"open": "^10.0.0",
|
|
20
|
+
"zod": "^3.23.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.0.0",
|
|
24
|
+
"tsup": "^8.0.0",
|
|
25
|
+
"typescript": "^5.7.0"
|
|
26
|
+
}
|
|
27
|
+
}
|