clawnify 0.1.1 → 0.1.4

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/README.md ADDED
@@ -0,0 +1,156 @@
1
+ <p align="center">
2
+ <a href="https://clawnify.com">
3
+ <img src="https://clawnify.com/favicon.png" height="64">
4
+ <h3 align="center">Clawnify</h3>
5
+ </a>
6
+ <p align="center">The easiest way to deploy internal software built with Claude Code, Codex, and Cursor.</p>
7
+ </p>
8
+
9
+ ## Clawnify CLI
10
+
11
+ Deploy full-stack software with a database to production in seconds. No infrastructure setup, no config files, no Cloudflare account needed.
12
+
13
+ Build with your AI coding tool, deploy with one command, share a live URL. Once you sign in to Clawnify, your team can access all their software and agents from one place — with managed auth across everything.
14
+
15
+ ```bash
16
+ npx clawnify init
17
+ cd my-app
18
+ npx clawnify deploy
19
+ ```
20
+
21
+ Your app is live at `https://my-app.apps.clawnify.com` with a D1 database, edge hosting, and a global CDN.
22
+
23
+ ## Quick Start
24
+
25
+ ### Create a new app
26
+
27
+ ```bash
28
+ npx clawnify init
29
+ ```
30
+
31
+ Pick a template (blank or CRUD), and you get a ready-to-deploy project:
32
+
33
+ ```
34
+ my-app/
35
+ src/
36
+ server/ # Hono API routes + D1 database
37
+ client/ # Preact frontend
38
+ clawnify.json # App manifest
39
+ wrangler.toml # Local dev config
40
+ ```
41
+
42
+ ### Develop locally
43
+
44
+ ```bash
45
+ cd my-app
46
+ pnpm install
47
+ pnpm dev
48
+ ```
49
+
50
+ Opens at `http://localhost:5173` with a local D1 database. Same code, same database interface — no conversion between local and production.
51
+
52
+ ### Deploy
53
+
54
+ ```bash
55
+ npx clawnify login
56
+ npx clawnify deploy
57
+ ```
58
+
59
+ That's it. Your app is live with:
60
+ - **Edge hosting** on Cloudflare Workers
61
+ - **D1 database** (SQLite at the edge)
62
+ - **Global CDN** for static assets
63
+ - **Custom URL** at `*.apps.clawnify.com`
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ # Run directly (recommended)
69
+ npx clawnify <command>
70
+
71
+ # Or install globally
72
+ npm i -g clawnify
73
+ ```
74
+
75
+ ## Commands
76
+
77
+ ```bash
78
+ clawnify init [dir] # Scaffold a new app
79
+ clawnify login # Authenticate with Clawnify
80
+ clawnify deploy [dir] # Deploy to production
81
+ clawnify deploy --from owner/repo # Deploy from a GitHub repo
82
+ clawnify ls # List your deployed apps
83
+ clawnify open <slug> # Open app in browser
84
+ clawnify logs <app-id> # View build logs
85
+ clawnify rm <app-id> # Delete an app
86
+ clawnify whoami # Show current user
87
+ ```
88
+
89
+ ## Deploy from GitHub
90
+
91
+ Any repo with a `clawnify.json` can be deployed:
92
+
93
+ ```bash
94
+ npx clawnify deploy --from clawnify/open-fieldservice
95
+ ```
96
+
97
+ ### `clawnify.json`
98
+
99
+ Add this to your repo root to make it deployable:
100
+
101
+ ```json
102
+ {
103
+ "$schema": "https://app.clawnify.com/schema/v1/clawnify.json",
104
+ "name": "My App",
105
+ "description": "What the app does",
106
+ "app": {
107
+ "framework": "preact+hono",
108
+ "database": true
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## Claude Code / MCP Integration
114
+
115
+ Use Clawnify as a tool in Claude Code:
116
+
117
+ ```bash
118
+ claude mcp add clawnify -- npx clawnify --mcp
119
+ ```
120
+
121
+ Then Claude Code can deploy apps directly:
122
+
123
+ ```
124
+ > Deploy this app to Clawnify
125
+ ```
126
+
127
+ Available MCP tools: `deploy`, `list_apps`, `get_app`, `delete_app`.
128
+
129
+ ## How It Works
130
+
131
+ ```
132
+ Your code → clawnify deploy → tar.gz upload → build on Cloudflare
133
+ → D1 database created
134
+ → Schema applied
135
+ → Vite builds frontend
136
+ → Deployed to Workers for Platforms
137
+ → Live at *.apps.clawnify.com
138
+ ```
139
+
140
+ **Local dev** uses `wrangler dev` which runs D1 as local SQLite — identical to production. No conversion, no surprises.
141
+
142
+ ## Frameworks
143
+
144
+ | Framework | Stack |
145
+ |-----------|-------|
146
+ | `preact+hono` | Preact frontend + Hono API + D1 |
147
+ | `react+hono` | React frontend + Hono API + D1 |
148
+ | `vite-preact` | Vite + Preact SPA |
149
+ | `static` | Static HTML/CSS/JS |
150
+
151
+ ## Links
152
+
153
+ - [Website](https://clawnify.com)
154
+ - [Dashboard](https://app.clawnify.com)
155
+ - [Deploy Button](https://app.clawnify.com/deploy) — one-click deploy for any repo with `clawnify.json`
156
+ - [JSON Schema](https://app.clawnify.com/schema/v1/clawnify.json) — editor autocomplete for `clawnify.json`
@@ -391,7 +391,7 @@ async function initCommand(dir) {
391
391
  private: true,
392
392
  type: "module",
393
393
  scripts: {
394
- dev: "wrangler dev",
394
+ dev: 'concurrently -n ui,api -c cyan,green "vite" "wrangler dev --port 8787"',
395
395
  build: "vite build"
396
396
  },
397
397
  dependencies: {
@@ -400,6 +400,7 @@ async function initCommand(dir) {
400
400
  },
401
401
  devDependencies: {
402
402
  "@preact/preset-vite": "^2.0.0",
403
+ concurrently: "^9.0.0",
403
404
  "vite": "^6.0.0",
404
405
  "wrangler": "^4.0.0"
405
406
  },
@@ -450,11 +451,6 @@ compatibility_flags = ["nodejs_compat"]
450
451
  binding = "DB"
451
452
  database_name = "${slug}-db"
452
453
  database_id = "local"
453
-
454
- [assets]
455
- directory = "./dist"
456
- binding = "ASSETS"
457
- not_found_handling = "single-page-application"
458
454
  `);
459
455
  write(targetDir, "index.html", `<!DOCTYPE html>
460
456
  <html lang="en">
@@ -543,7 +539,7 @@ render(<App />, document.getElementById("app")!);
543
539
  console.log(" Get started:");
544
540
  console.log(` cd ${path2.relative(process.cwd(), targetDir)}`);
545
541
  console.log(" pnpm install");
546
- console.log(" pnpm dev # local dev at http://localhost:8787");
542
+ console.log(" pnpm dev # local dev at http://localhost:5173");
547
543
  console.log(" clawnify deploy # deploy to production");
548
544
  console.log();
549
545
  }
@@ -738,4 +734,4 @@ function runCli() {
738
734
  export {
739
735
  runCli
740
736
  };
741
- //# sourceMappingURL=cli-MDRNA6W2.js.map
737
+ //# sourceMappingURL=cli-DUPWH2W3.js.map
@@ -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: \"concurrently -n ui,api -c cyan,green \\\"vite\\\" \\\"wrangler dev --port 8787\\\"\",\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 concurrently: \"^9.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 only — deploy builds its own)\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\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:5173\");\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":";;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;;;ACAxB,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,OAAO,cAAc;AACrB,OAAO,UAAU;AAyBjB,eAAsB,eAA8B;AAClD,QAAM,EAAE,cAAc,UAAU,eAAe,UAAU,IAAI,aAAa;AAC1E,QAAM,QAAQ,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACnD,QAAM,OAAO;AACb,QAAM,cAAc,oBAAoB,IAAI;AAE5C,QAAM,UAAU,IAAI,IAAI,mBAAmB;AAC3C,UAAQ,aAAa,IAAI,iBAAiB,MAAM;AAChD,UAAQ,aAAa,IAAI,aAAa,eAAe;AACrD,UAAQ,aAAa,IAAI,gBAAgB,WAAW;AACpD,UAAQ,aAAa,IAAI,kBAAkB,SAAS;AACpD,UAAQ,aAAa,IAAI,yBAAyB,MAAM;AACxD,UAAQ,aAAa,IAAI,SAAS,cAAc;AAChD,UAAQ,aAAa,IAAI,SAAS,KAAK;AAEvC,QAAM,SAAS,MAAM,IAAI,QAItB,CAAC,SAAS,WAAW;AACtB,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,sCAAsC,CAAC;AAAA,IAC1D,GAAG,IAAI,KAAK,GAAI;AAEhB,UAAM,SAAS,KAAK,aAAa,OAAO,KAAK,QAAQ;AACnD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,oBAAoB,IAAI,EAAE;AACxD,UAAI,IAAI,aAAa,aAAa;AAChC,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI,WAAW;AACnB;AAAA,MACF;AAEA,YAAM,OAAO,IAAI,aAAa,IAAI,MAAM;AACxC,YAAM,gBAAgB,IAAI,aAAa,IAAI,OAAO;AAClD,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAE1C,UAAI,OAAO;AACT,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,YAAI;AAAA,UACF;AAAA,QACF;AACA,qBAAa,OAAO;AACpB,eAAO,MAAM;AACb,eAAO,IAAI,MAAM,yBAAyB,KAAK,EAAE,CAAC;AAClD;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,kBAAkB,OAAO;AACpC,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,YAAI;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF;AAGA,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,iBAAiB;AAAA,UAC5C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,UAC/D,MAAM,IAAI,gBAAgB;AAAA,YACxB,YAAY;AAAA,YACZ;AAAA,YACA,WAAW;AAAA,YACX,cAAc;AAAA,YACd,eAAe;AAAA,UACjB,CAAC;AAAA,QACH,CAAC;AAED,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,MAAM,MAAM,SAAS,KAAK;AAChC,gBAAM,IAAI,MAAM,0BAA0B,GAAG,EAAE;AAAA,QACjD;AAEA,cAAM,SAAU,MAAM,SAAS,KAAK;AAOpC,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,YAAI;AAAA,UACF;AAAA,QACF;AAEA,qBAAa,OAAO;AACpB,eAAO,MAAM;AACb,gBAAQ,MAAM;AAAA,MAChB,SAAS,KAAK;AACZ,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,YAAI;AAAA,UACF;AAAA,QACF;AACA,qBAAa,OAAO;AACpB,eAAO,MAAM;AACb,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAED,WAAO,OAAO,MAAM,MAAM;AACxB,cAAQ,IAAI,iCAAiC;AAC7C,WAAK,QAAQ,SAAS,CAAC;AACvB,cAAQ,IAAI,8BAA8B;AAAA,IAC5C,CAAC;AAAA,EACH,CAAC;AAGD,MAAI,QAAQ;AACZ,MAAI;AACF,UAAM,UAAU,KAAK;AAAA,MACnB,OAAO,KAAK,OAAO,aAAa,MAAM,GAAG,EAAE,CAAC,GAAG,QAAQ,EAAE,SAAS;AAAA,IACpE;AACA,YAAQ,QAAQ,SAAS;AAAA,EAC3B,QAAQ;AAAA,EAER;AAEA,QAAM,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,OAAO;AAGzD,WAAS;AAAA,IACP,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,YAAY;AAAA,EACd,CAAC;AAGD,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,IAAI,IAAI,UAAU;AACxC,QAAI,QAAQ,IAAI;AACd,YAAM,EAAE,KAAK,IAAK,MAAM,QAAQ,KAAK;AAIrC,UAAI,KAAK,WAAW,GAAG;AACrB,gBAAQ,KAAK,CAAC,EAAE;AAChB,gBAAQ,IAAI,iBAAiB,KAAK,CAAC,EAAE,IAAI,EAAE;AAAA,MAC7C,WAAW,KAAK,SAAS,GAAG;AAC1B,gBAAQ,IAAI,6BAA6B;AACzC,aAAK,QAAQ,CAAC,KAAK,MAAM;AACvB,kBAAQ,IAAI,KAAK,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG;AAAA,QACrD,CAAC;AACD,gBAAQ,IAAI;AAEZ,cAAM,KAAK,SAAS,gBAAgB;AAAA,UAClC,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,QAClB,CAAC;AAED,cAAM,SAAS,MAAM,IAAI,QAAgB,CAAC,YAAY;AACpD,aAAG,SAAS,kBAAkB,CAAC,WAAW;AACxC,eAAG,MAAM;AACT,oBAAQ,OAAO,KAAK,CAAC;AAAA,UACvB,CAAC;AAAA,QACH,CAAC;AAED,cAAM,QAAQ,SAAS,QAAQ,EAAE,IAAI;AACrC,YAAI,SAAS,KAAK,QAAQ,KAAK,QAAQ;AACrC,kBAAQ,KAAK,KAAK,EAAE;AACpB,kBAAQ,IAAI,aAAa,KAAK,KAAK,EAAE,IAAI,EAAE;AAAA,QAC7C,OAAO;AACL,kBAAQ,KAAK,CAAC,EAAE;AAChB,kBAAQ,IAAI,kBAAkB,KAAK,CAAC,EAAE,IAAI,EAAE;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,WAAS;AAAA,IACP,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AAED,UAAQ,IAAI,gBAAgB,KAAK,EAAE;AACrC;;;AClNA,OAAO,UAAU;AAKjB,eAAsB,cACpB,KACA,SACe;AACf,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI,SAAS,MAAM;AACjB,UAAM,OAAO,QAAQ,KAAK,QAAQ,YAAY,EAAE;AAChD,YAAQ,IAAI,kBAAkB,IAAI,KAAK;AAEvC,UAAM,MAAM,MAAM,IAAI,KAAK,mBAAmB,EAAE,MAAM,QAAQ,OAAO,CAAC;AACtE,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAO,MAAM,IAAI,KAAK;AAC5B,cAAQ,MAAM,kBAAkB,IAAI,SAAS,IAAI,UAAU,EAAE;AAC7D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,YAAQ,KAAK;AACb,WAAO,KAAK;AACZ,eAAW,KAAK;AAAA,EAClB,OAAO;AACL,UAAM,cAAc,MAAM,KAAK,QAAQ,GAAG,IAAI,QAAQ,IAAI;AAC1D,UAAM,WAAW,aAAa,WAAW;AACzC,UAAM,OAAO,SAAS,QAAQ,UAAU,QAAQ,KAAK,SAAS,WAAW;AACzE,UAAM,YAAY,UAAU,KAAK,aAAa;AAE9C,YAAQ,IAAI,aAAa,IAAI,KAAK;AAElC,UAAM,UAAU,cAAc,WAAW;AACzC,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,UAAU,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,eAAe;AAC9D,aAAS,OAAO,QAAQ,IAAI;AAC5B,aAAS,OAAO,aAAa,SAAS;AAEtC,UAAM,MAAM,MAAM,IAAI,SAAS,mBAAmB,QAAQ;AAC1D,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAO,MAAM,IAAI,KAAK;AAC5B,cAAQ,MAAM,kBAAkB,IAAI,SAAS,IAAI,UAAU,EAAE;AAC7D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,YAAQ,KAAK;AACb,WAAO,KAAK;AACZ,eAAW,KAAK;AAEhB,gBAAY,aAAa;AAAA,MACvB,QAAQ;AAAA,MACR;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,eAAe,KAAK;AAEzC,MAAI,OAAO,WAAW,SAAS;AAC7B,YAAQ,MAAM;AAAA,mBAAsB,OAAO,SAAS,eAAe;AAAA,CAAI;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,WAAW,IAAI;AAC3B,UAAQ,IAAI;AAAA,sBAAoB,GAAG;AAAA,CAAI;AACvC,UAAQ,IAAI,0BAA0B,IAAI;AAAA,CAAI;AAChD;;;ACtEA,eAAsB,YAA2B;AAC/C,QAAM,MAAM,MAAM,IAAI,IAAI,UAAU;AACpC,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,sBAAsB;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,MAAI,KAAK,WAAW,GAAG;AACrB,YAAQ,IAAI,uBAAuB;AACnC;AAAA,EACF;AAEA,QAAM,YAAY,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAC/D,QAAM,YAAY,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAE/D,UAAQ;AAAA,IACN,GAAG,OAAO,OAAO,SAAS,CAAC,KAAK,OAAO,OAAO,SAAS,CAAC,KAAK,SAAS,OAAO,CAAC,CAAC;AAAA,EACjF;AACA,UAAQ;AAAA,IACN,GAAG,SAAI,OAAO,SAAS,CAAC,KAAK,SAAI,OAAO,SAAS,CAAC,KAAK,SAAI,OAAO,CAAC,CAAC,KAAK,SAAI,OAAO,EAAE,CAAC;AAAA,EACzF;AAEA,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,WAAW,IAAI,IAAI;AAC/B,YAAQ;AAAA,MACN,GAAG,IAAI,KAAK,OAAO,SAAS,CAAC,KAAK,IAAI,KAAK,OAAO,SAAS,CAAC,KAAK,IAAI,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AAAA,IAC/F;AAAA,EACF;AACF;;;AC/BA,eAAsB,YAAY,OAA8B;AAC9D,QAAM,MAAM,MAAM,IAAI,IAAI,YAAY,KAAK,OAAO;AAClD,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,uBAAuB;AACrC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,UAAQ,IAAI,WAAW,KAAK,MAAM,EAAE;AACpC,MAAI,KAAK,gBAAgB;AACvB,YAAQ,IAAI,YAAY,KAAK,cAAc,EAAE;AAAA,EAC/C;AACA,MAAI,KAAK,MAAM;AACb,YAAQ,IAAI,wBAAwB;AACpC,YAAQ,IAAI,KAAK,IAAI;AAAA,EACvB;AACF;;;ACzBA,OAAOA,eAAc;AAOrB,eAAsB,UAAU,OAA8B;AAC5D,QAAM,MAAM,MAAM,IAAI,IAAI,YAAY,KAAK,EAAE;AAC7C,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,gBAAgB;AAC9B,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAO,MAAM,IAAI,KAAK;AAE5B,QAAM,YAAY,MAAM,IAAI,QAAiB,CAAC,YAAY;AACxD,UAAM,KAAKC,UAAS,gBAAgB;AAAA,MAClC,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IAClB,CAAC;AACD,OAAG,SAAS,UAAU,IAAI,IAAI,YAAY,CAAC,WAAW;AACpD,SAAG,MAAM;AACT,cAAQ,OAAO,YAAY,MAAM,GAAG;AAAA,IACtC,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,WAAW;AACd,YAAQ,IAAI,YAAY;AACxB;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,IAAI,IAAI,YAAY,KAAK,EAAE;AAChD,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,uBAAuB;AACrC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,WAAW,IAAI,IAAI,EAAE;AACnC;;;ACvCA,OAAOC,WAAU;AASjB,eAAsB,YAAY,MAA6B;AAC7D,MAAI;AAEJ,QAAM,UAAU,YAAY;AAC5B,MAAI,SAAS,SAAS,QAAQ,QAAQ,WAAW;AAC/C,eAAW,QAAQ;AAAA,EACrB;AAEA,MAAI,CAAC,UAAU;AACb,UAAM,MAAM,MAAM,IAAI,IAAI,UAAU;AACpC,QAAI,IAAI,IAAI;AACV,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,YAAM,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC5C,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,IAAI;AACzB,MAAI,UAAU;AACZ,WAAO,UAAU,QAAQ;AAAA,EAC3B;AAEA,QAAMC,MAAK,GAAG;AACd,UAAQ,IAAI,mBAAmB;AACjC;;;AC/BA,eAAsB,gBAA+B;AACnD,QAAM,OAAO,SAAS;AAEtB,MAAI,CAAC,MAAM;AACT,YAAQ,IAAI,eAAe;AAC3B;AAAA,EACF;AAEA,QAAM,QAAQ,kBAAkB,KAAK,YAAY,KAAK;AACtD,UAAQ,IAAI,WAAW,KAAK,EAAE;AAC9B,MAAI,KAAK,QAAQ;AACf,YAAQ,IAAI,WAAW,KAAK,MAAM,EAAE;AAAA,EACtC;AACF;;;ACfA,OAAO,QAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,eAAc;AAErB,SAAS,IAAI,UAAmC;AAC9C,QAAM,KAAKA,UAAS,gBAAgB;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AACD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,SAAG,MAAM;AACT,cAAQ,OAAO,KAAK,CAAC;AAAA,IACvB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,QAAQ,MAAsB;AACrC,SAAO,KACJ,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,UAAU,EAAE,EACpB,MAAM,GAAG,EAAE;AAChB;AAOA,eAAsB,YAAY,KAA6B;AAC7D,QAAM,OAAQ,MAAM,IAAI,YAAY,KAAM;AAC1C,QAAM,OAAO,QAAQ,IAAI;AACzB,QAAM,YAAY,MAAMC,MAAK,QAAQ,GAAG,IAAIA,MAAK,QAAQ,IAAI;AAE7D,UAAQ,IAAI;AACZ,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,8DAAyD;AACrE,UAAQ,IAAI,0DAAqD;AACjE,UAAQ,IAAI;AACZ,QAAM,iBAAkB,MAAM,IAAI,gBAAgB,KAAM;AACxD,QAAM,WAAW,mBAAmB,MAAM,SAAS;AAEnD,MAAI,GAAG,WAAW,SAAS,KAAK,GAAG,YAAY,SAAS,EAAE,SAAS,GAAG;AACpE,YAAQ,MAAM,aAAa,SAAS,gBAAgB;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,KAAG,UAAUA,MAAK,KAAK,WAAW,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AACpE,KAAG,UAAUA,MAAK,KAAK,WAAW,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAGpE,QAAM,WAAW,iBAAiB,KAAK,UAAU;AAAA,IAC/C,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,IACA,aAAa;AAAA,IACb,KAAK,EAAE,WAAW,eAAe,UAAU,MAAM,SAAS,MAAM;AAAA,EAClE,GAAG,MAAM,CAAC,CAAC;AAGX,QAAM,WAAW,gBAAgB,KAAK,UAAU;AAAA,IAC9C,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,IACA,iBAAiB;AAAA,MACf,uBAAuB;AAAA,MACvB,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAY;AAAA,IACd;AAAA,IACA,MAAM,EAAE,uBAAuB,CAAC,SAAS,EAAE;AAAA,EAC7C,GAAG,MAAM,CAAC,CAAC;AAGX,QAAM,WAAW,iBAAiB,KAAK,UAAU;AAAA,IAC/C,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,kBAAkB;AAAA,MAClB,QAAQ;AAAA,MACR,KAAK,CAAC,UAAU,OAAO,cAAc;AAAA,MACrC,KAAK;AAAA,MACL,iBAAiB;AAAA,MACjB,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,mBAAmB;AAAA,MACnB,iBAAiB;AAAA,IACnB;AAAA,IACA,SAAS,CAAC,eAAe,cAAc;AAAA,IACvC,SAAS,CAAC,gBAAgB,MAAM;AAAA,EAClC,GAAG,MAAM,CAAC,CAAC;AAGX,QAAM,WAAW,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAkBpC;AAGC,QAAM,WAAW,iBAAiB,WAAW,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAOhC,IAAI;AAAA;AAAA,CAEtB;AAGC,QAAM,WAAW,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,WAKtB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAOd;AAGC,QAAM,WAAW,cAAc;AAAA;AAAA;AAAA;AAAA,CAIhC;AAGC,QAAM,WAAW,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAmCtC;AAGC,QAAM,WAAW,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAgBzC;AAGC,QAAM,WAAW,uBAAuB;AAAA;AAAA;AAAA;AAAA,CAIzC;AAEC,MAAI,aAAa,QAAQ;AACvB,sBAAkB,WAAW,IAAI;AAAA,EACnC,OAAO;AACL,uBAAmB,WAAW,IAAI;AAAA,EACpC;AAEA,UAAQ,IAAI;AACZ,UAAQ,IAAI,aAAa,IAAI,OAAOA,MAAK,SAAS,QAAQ,IAAI,GAAG,SAAS,CAAC,GAAG;AAC9E,UAAQ,IAAI;AACZ,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,IAAI,UAAUA,MAAK,SAAS,QAAQ,IAAI,GAAG,SAAS,CAAC,EAAE;AAC/D,UAAQ,IAAI,kBAAkB;AAC9B,UAAQ,IAAI,4DAA4D;AACxE,UAAQ,IAAI,8CAA8C;AAC1D,UAAQ,IAAI;AACd;AAEA,SAAS,mBAAmB,KAAa,MAAc;AACrD,QAAM,KAAK,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA,CAKrC;AAEC,QAAM,KAAK,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAWpC;AAEC,QAAM,KAAK,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAWvB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAUf;AACD;AAEA,SAAS,kBAAkB,KAAa,MAAc;AACpD,QAAM,KAAK,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMrC;AAEC,QAAM,KAAK,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAqCpC;AAEC,QAAM,KAAK,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAiDvB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CA6Bf;AACD;AAEA,SAAS,MAAM,KAAa,MAAc,SAAiB;AACzD,QAAM,WAAWA,MAAK,KAAK,KAAK,IAAI;AACpC,KAAG,UAAUA,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,KAAG,cAAc,UAAU,OAAO;AACpC;;;ARrZO,SAAS,SAAS;AACvB,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,UAAU,EACf,YAAY,yBAAyB,EACrC,QAAQ,OAAO;AAElB,UACG,QAAQ,YAAY,EACpB,YAAY,6BAA6B,EACzC,OAAO,WAAW;AAErB,UACG,QAAQ,OAAO,EACf,YAAY,4BAA4B,EACxC,OAAO,YAAY;AAEtB,UACG,QAAQ,cAAc,EACtB,YAAY,2BAA2B,EACvC,OAAO,iBAAiB,wCAAwC,EAChE,OAAO,iBAAiB,mBAAmB,EAC3C,OAAO,aAAa;AAEvB,UACG,QAAQ,IAAI,EACZ,YAAY,oBAAoB,EAChC,OAAO,SAAS;AAEnB,UACG,QAAQ,eAAe,EACvB,YAAY,4BAA4B,EACxC,OAAO,WAAW;AAErB,UACG,QAAQ,aAAa,EACrB,YAAY,eAAe,EAC3B,OAAO,SAAS;AAEnB,UACG,QAAQ,aAAa,EACrB,YAAY,4BAA4B,EACxC,OAAO,WAAW;AAErB,UACG,QAAQ,QAAQ,EAChB,YAAY,wBAAwB,EACpC,OAAO,aAAa;AAEvB,UAAQ,MAAM;AAChB;","names":["readline","readline","open","open","path","readline","path"]}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ if (process.argv.includes("--mcp")) {
5
5
  const { startMcpServer } = await import("./mcp-JLE4IWRX.js");
6
6
  await startMcpServer();
7
7
  } else {
8
- const { runCli } = await import("./cli-MDRNA6W2.js");
8
+ const { runCli } = await import("./cli-DUPWH2W3.js");
9
9
  runCli();
10
10
  }
11
11
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawnify",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Deploy apps to Clawnify from the terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
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"]}