@aerostack/cli 1.3.5 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,6 +29,21 @@ npx aerostack deploy
29
29
 
30
30
  The first run downloads the binary from GitHub releases. Subsequent runs use the cached binary at `~/.aerostack/bin` (shared with the curl install).
31
31
 
32
+ ## Registry: add community functions
33
+
34
+ Install open-source functions from the Aerostack registry into your project (Cloudflare or Node.js):
35
+
36
+ ```bash
37
+ npx aerostack add stripe-checkout
38
+ npx aerostack add alice/stripe-checkout
39
+ npx aerostack add stripe-checkout --runtime=node
40
+ ```
41
+
42
+ - **Cloudflare** (default): Adds Hono + D1 adapter and wires the route. Uses Drizzle in `src/db/` when the function has a schema.
43
+ - **Node.js** (`--runtime=node`): Adds a Node/Express adapter. Pass your own Drizzle client (pg or sqlite) and mount the router in your app.
44
+
45
+ Functions follow the **Open Function Standard**: one portable core, multiple adapters. You can use them in any Node.js project, not only on the Aerostack platform. See `planning/OPEN_FUNCTION_STANDARD.md` for the spec.
46
+
32
47
  ## Uninstall
33
48
 
34
49
  ```bash
@@ -1,41 +1,10 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Postinstall: print PATH fix if aerostack bin not in PATH.
4
- */
5
- const path = require("path");
6
- const fs = require("fs");
7
-
8
- // Assuming there's a download function that might be added or already exists
9
- // and the user wants to wrap its execution.
10
- // For the purpose of this edit, we'll assume `downloadBinary()` is a placeholder
11
- // for the actual download logic that would be placed here.
12
- try {
13
- // Original download logic (placeholder, as it's not in the provided original code)
14
- // If downloadBinary() is not defined, this will cause a ReferenceError.
15
- // The user's instruction implies this function exists or will be added.
16
- // downloadBinary(); // Uncomment and define if actual download logic is intended here
17
- } catch (e) {
18
- console.warn('⚠️ Aerostack CLI download failed. You may need to install it manually or check your connection.');
19
- console.warn(' Details: ' + e.message);
20
- // Do not exit with error, allow install to continue
21
- process.exit(0);
22
- }
23
-
24
- // Global install: package at prefix/node_modules/aerostack, bin at prefix/bin
25
- const pkgRoot = path.resolve(__dirname, "..");
26
- const prefix = path.dirname(path.dirname(pkgRoot));
27
- const binDir = path.join(prefix, "bin");
28
- const binPath = path.join(binDir, process.platform === "win32" ? "aerostack.cmd" : "aerostack");
29
-
30
- if (!fs.existsSync(binDir)) return;
31
-
32
- const pathEnv = (process.env.PATH || "").split(path.delimiter);
33
- const inPath = pathEnv.some((p) => path.resolve(p) === path.resolve(binDir));
34
-
35
- if (!inPath) {
36
- console.log("\n\u001b[33mAerostack installed. Add to PATH (run once, or add to ~/.zshrc):\u001b[0m");
37
- console.log(` \u001b[36mexport PATH="$PATH:${binDir}"\u001b[0m\n`);
38
- console.log("Or use \u001b[32mnpx aerostack\u001b[0m (no PATH config needed).\n");
39
- } else {
40
- console.log("\n\u001b[32mAerostack installed. Run: aerostack --version\u001b[0m\n");
2
+ import { existsSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ // Just a polite notice
6
+ const binDir = join(process.env.HOME || process.env.USERPROFILE || '.', '.aerostack', 'bin');
7
+ if (existsSync(binDir)) {
8
+ console.log(`\nAerostack is installed.`);
9
+ console.log(`To use the binary directly, add ${binDir} to your PATH.\n`);
41
10
  }
package/bin/run.js CHANGED
@@ -1,20 +1,55 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Aerostack CLI - npm/pnpm/yarn wrapper
4
- * Downloads the Go binary from GitHub releases on first run.
3
+ * Aerostack CLI - Hybrid Dispatcher
4
+ *
5
+ * Routes specific commands (init, add, publish, list, login) to the
6
+ * pure Node.js implementation (drizzle/hono support).
7
+ *
8
+ * All other commands map to the Go binary (legacy/platform).
5
9
  */
6
10
 
7
- const { spawn } = require("child_process");
8
- const fs = require("fs");
9
- const path = require("path");
11
+ import { spawn } from 'child_process';
12
+ import { existsSync, mkdirSync, chmodSync, writeFileSync, rmSync, renameSync } from 'fs';
13
+ import { join, resolve } from 'path';
14
+ import { platform as getOsPlatform, arch as getOsArch, tmpdir } from 'os';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
18
+
19
+ // ─── 1. Hybrid Dispatcher ──────────────────────────────────────────────────────
20
+
21
+ const args = process.argv.slice(2);
22
+ const command = args[0];
23
+ const NODE_COMMANDS = ['init', 'add', 'publish', 'list', 'login'];
24
+
25
+ if (command && NODE_COMMANDS.includes(command)) {
26
+ // Dispatch to Node.js implementation
27
+ import('../dist/index.js').then(m => m.run(args)).catch(err => {
28
+ console.error('Failed to run Node.js CLI:', err);
29
+ process.exit(1);
30
+ });
31
+ } else {
32
+ // Dispatch to Go implementation
33
+ ensureBinary().then(binPath => {
34
+ const child = spawn(binPath, args, { stdio: 'inherit' });
35
+ child.on('exit', code => process.exit(code ?? 0));
36
+ }).catch(err => {
37
+ console.error('Error:', err.message);
38
+ process.exit(1);
39
+ });
40
+ }
41
+
42
+ // ─── 2. Go Binary Downloader (Legacy) ──────────────────────────────────────────
10
43
 
11
44
  const REPO = "aerostackdev/cli";
12
45
  const BINARY = "aerostack";
13
- const INSTALL_DIR = process.env.AEROSTACK_INSTALL_DIR || path.join(process.env.HOME || process.env.USERPROFILE, ".aerostack", "bin");
46
+ // Use HOME or USERPROFILE for global install location
47
+ const HOME = process.env.HOME || process.env.USERPROFILE || tmpdir();
48
+ const INSTALL_DIR = process.env.AEROSTACK_INSTALL_DIR || join(HOME, ".aerostack", "bin");
14
49
 
15
50
  function getPlatform() {
16
- const platform = process.platform;
17
- const arch = process.arch;
51
+ const platform = getOsPlatform();
52
+ const arch = getOsArch();
18
53
  const map = {
19
54
  darwin: { arm64: "darwin_arm64", x64: "darwin_amd64" },
20
55
  linux: { arm64: "linux_arm64", x64: "linux_amd64" },
@@ -26,102 +61,69 @@ function getPlatform() {
26
61
  }
27
62
 
28
63
  async function getLatestVersion() {
29
- const res = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
30
- headers: { "User-Agent": "aerostack-cli-npm/1.0" },
31
- });
32
- if (!res.ok) throw new Error("Failed to fetch latest version");
33
- const data = await res.text();
34
- const m = data.match(/"tag_name":\s*"v([^"]+)"/);
35
- if (!m) throw new Error("Could not parse version");
36
- return m[1];
64
+ // If we can't check, fallback or fail. Here we try checking.
65
+ try {
66
+ const res = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
67
+ headers: { "User-Agent": "aerostack-cli-npm/1.5.0" },
68
+ });
69
+ if (!res.ok) return "1.3.0"; // Fallback if rate limited
70
+ const data = await res.json();
71
+ return data.tag_name.replace(/^v/, '');
72
+ } catch {
73
+ return "1.3.0";
74
+ }
37
75
  }
38
76
 
39
77
  async function downloadBinary(version, asset) {
40
78
  const ext = asset.startsWith("windows") ? "zip" : "tar.gz";
41
- const archive = `${BINARY}_${version}_${asset}.${ext}`;
42
- const url = `https://github.com/${REPO}/releases/download/v${version}/${archive}`;
79
+ const archive = `${BINARY}_v${version}_${asset}.${ext}`; // Check github release naming convention
80
+ // Usually: aerostack_1.3.0_darwin_arm64.tar.gz
81
+ // Let's assume standard GoReleaser naming: name_version_os_arch
82
+ const releaseName = `${BINARY}_${version}_${asset}.${ext}`;
43
83
 
44
- const tmpDir = path.join(require("os").tmpdir(), `aerostack-${Date.now()}`);
45
- fs.mkdirSync(tmpDir, { recursive: true });
46
- const archivePath = path.join(tmpDir, archive);
84
+ const url = `https://github.com/${REPO}/releases/download/v${version}/${releaseName}`;
85
+ const tmpDir = join(tmpdir(), `aerostack-${Date.now()}`);
86
+ mkdirSync(tmpDir, { recursive: true });
87
+
88
+ const archivePath = join(tmpDir, releaseName);
89
+
90
+ console.error(`Downloading Aerostack Go Core v${version}...`);
91
+ const res = await fetch(url, { redirect: "follow" });
92
+ if (!res.ok) throw new Error(`Download failed: ${url} (${res.status})`);
47
93
 
48
- const res = await fetch(url, {
49
- headers: { "User-Agent": "aerostack-cli-npm/1.0" },
50
- redirect: "follow",
51
- });
52
- if (!res.ok) throw new Error(`Download failed: ${url}`);
53
94
  const buffer = Buffer.from(await res.arrayBuffer());
54
- fs.writeFileSync(archivePath, buffer);
95
+ writeFileSync(archivePath, buffer);
55
96
 
56
97
  const binDir = INSTALL_DIR;
57
- fs.mkdirSync(binDir, { recursive: true });
58
- const binPath = path.join(binDir, process.platform === "win32" ? `${BINARY}.exe` : BINARY);
98
+ mkdirSync(binDir, { recursive: true });
99
+ const binPath = join(binDir, process.platform === "win32" ? `${BINARY}.exe` : BINARY);
59
100
 
60
101
  if (ext === "zip") {
61
- const AdmZip = require("adm-zip");
102
+ const AdmZip = (await import("adm-zip")).default;
62
103
  const zip = new AdmZip(archivePath);
63
104
  zip.extractAllTo(tmpDir, true);
64
- const extracted = path.join(tmpDir, `${BINARY}.exe`);
65
- fs.renameSync(extracted, binPath);
105
+ const extracted = join(tmpDir, `${BINARY}.exe`);
106
+ if (existsSync(extracted)) renameSync(extracted, binPath);
66
107
  } else {
67
- const tar = require("tar");
108
+ const tar = (await import("tar")).default;
68
109
  await tar.x({ file: archivePath, cwd: tmpDir });
69
- fs.renameSync(path.join(tmpDir, BINARY), binPath);
110
+ const extracted = join(tmpDir, BINARY);
111
+ if (existsSync(extracted)) renameSync(extracted, binPath);
70
112
  }
71
113
 
72
- fs.chmodSync(binPath, 0o755);
73
- fs.rmSync(tmpDir, { recursive: true, force: true });
114
+ chmodSync(binPath, 0o755);
115
+ try { rmSync(tmpDir, { recursive: true, force: true }); } catch { }
74
116
  return binPath;
75
117
  }
76
118
 
77
- async function getInstalledVersion(binPath) {
78
- try {
79
- const { execFileSync } = require("child_process");
80
- const out = execFileSync(binPath, ["--version"], { encoding: "utf8" });
81
- const m = out.match(/v?(\d+\.\d+\.\d+)/);
82
- return m ? m[1] : null;
83
- } catch {
84
- return null;
85
- }
86
- }
87
-
88
119
  async function ensureBinary() {
89
- const binPath = path.join(INSTALL_DIR, process.platform === "win32" ? `${BINARY}.exe` : BINARY);
90
- const { platform, arch, asset } = getPlatform();
91
- const latestVersion = await getLatestVersion();
92
-
93
- if (fs.existsSync(binPath)) {
94
- const installed = await getInstalledVersion(binPath);
95
- if (installed && installed === latestVersion) return binPath;
96
- if (installed) console.error(`Updating Aerostack CLI v${installed} → v${latestVersion}...`);
97
- } else {
98
- console.error(`Downloading Aerostack CLI v${latestVersion}...`);
99
- }
120
+ const binPath = join(INSTALL_DIR, process.platform === "win32" ? `${BINARY}.exe` : BINARY);
121
+ const { arch, asset } = getPlatform();
100
122
 
101
- try {
102
- return await downloadBinary(latestVersion, asset);
103
- } catch (err) {
104
- // Windows on ARM64 can run x64 binaries via emulation.
105
- // If arm64 artifact is unavailable, fall back to windows_amd64.
106
- if (platform === "win32" && arch === "arm64" && asset === "windows_arm64") {
107
- return downloadBinary(latestVersion, "windows_amd64");
108
- }
109
- throw err;
110
- }
111
- }
123
+ // For now, always try to grab latest or fallback
124
+ // In production we should cache version check
125
+ if (existsSync(binPath)) return binPath;
112
126
 
113
- async function main() {
114
- try {
115
- const binPath = await ensureBinary();
116
- const child = spawn(binPath, process.argv.slice(2), {
117
- stdio: "inherit",
118
- shell: false,
119
- });
120
- child.on("exit", (code) => process.exit(code ?? 0));
121
- } catch (err) {
122
- console.error("Error:", err.message);
123
- process.exit(1);
124
- }
127
+ const version = await getLatestVersion();
128
+ return await downloadBinary(version, asset);
125
129
  }
126
-
127
- main();
package/package.json CHANGED
@@ -1,20 +1,33 @@
1
1
  {
2
2
  "name": "@aerostack/cli",
3
- "version": "1.3.5",
3
+ "version": "1.5.0",
4
4
  "description": "Aerostack CLI - Zero-config serverless development for Cloudflare",
5
+ "type": "module",
5
6
  "files": [
6
- "bin"
7
+ "bin",
8
+ "dist",
9
+ "templates"
7
10
  ],
8
11
  "bin": {
9
12
  "aerostack": "bin/run.js"
10
13
  },
11
14
  "scripts": {
15
+ "build": "tsc",
12
16
  "postinstall": "node bin/postinstall.js",
13
17
  "uninstall": "node bin/uninstall.js"
14
18
  },
15
19
  "dependencies": {
16
20
  "adm-zip": "^0.5.16",
17
- "tar": "^7.0.0"
21
+ "tar": "^7.0.0",
22
+ "chalk": "^5.3.0",
23
+ "ora": "^8.0.1",
24
+ "prompts": "^2.4.2",
25
+ "node-fetch": "^3.3.2"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.0.0",
29
+ "@types/prompts": "^2.4.9",
30
+ "typescript": "^5.9.3"
18
31
  },
19
32
  "keywords": [
20
33
  "aerostack",
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+
3
+ export default defineConfig({
4
+ schema: [
5
+ './src/db/schema.ts', // Base/core tables
6
+ './src/modules/**/schema.ts' // Auto-discover all installed module schemas
7
+ ],
8
+ out: './drizzle',
9
+ dialect: 'sqlite',
10
+ driver: 'd1-http',
11
+ dbCredentials: {
12
+ accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
13
+ databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
14
+ token: process.env.CLOUDFLARE_D1_TOKEN!,
15
+ },
16
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "wrangler dev",
8
+ "deploy": "wrangler deploy",
9
+ "db:push": "drizzle-kit push",
10
+ "db:generate": "drizzle-kit generate",
11
+ "db:studio": "drizzle-kit studio"
12
+ },
13
+ "dependencies": {
14
+ "drizzle-orm": "^0.45.1",
15
+ "hono": "^4.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@cloudflare/workers-types": "^4.20250219.0",
19
+ "drizzle-kit": "^0.31.0",
20
+ "typescript": "^5.9.0",
21
+ "wrangler": "^4.0.0"
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ import { drizzle } from 'drizzle-orm/d1';
2
+ import * as schema from './schema';
3
+
4
+ export type DrizzleDB = ReturnType<typeof createDb>;
5
+
6
+ /**
7
+ * Creates a Drizzle database client bound to the Cloudflare D1 instance.
8
+ * Call this inside each request handler: const db = createDb(c.env.DB);
9
+ */
10
+ export function createDb(d1: D1Database) {
11
+ return drizzle(d1, { schema });
12
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * src/db/schema.ts — Central Schema Hub
3
+ *
4
+ * This file re-exports all table schemas from installed modules.
5
+ * Drizzle uses this for type-safe query building.
6
+ *
7
+ * When you run `npx aerostack add <module>`, the module's schema
8
+ * is imported here automatically.
9
+ */
10
+
11
+ // aerostack:schema-imports — module schemas are auto-imported above this line
12
+
13
+ // Export everything for Drizzle's relational queries
14
+ // aerostack:schema-exports — exports are auto-added above this line
@@ -0,0 +1,28 @@
1
+ /**
2
+ * src/index.ts — Aerostack Base Project
3
+ *
4
+ * This is the main entry point for your Cloudflare Worker.
5
+ * Routes from installed modules are mounted here automatically
6
+ * when you run `npx aerostack add <function-name>`.
7
+ */
8
+ import { Hono } from 'hono';
9
+
10
+ // ─── Module Routes ─────────────────────────────────────────────────────────────
11
+ // aerostack:imports — imports are auto-injected above this line
12
+
13
+ const app = new Hono<{
14
+ Bindings: {
15
+ DB: D1Database;
16
+ CACHE: KVNamespace;
17
+ AI: Ai;
18
+ [key: string]: unknown;
19
+ }
20
+ }>();
21
+
22
+ // ─── Installed Module Routes ──────────────────────────────────────────────────
23
+ // aerostack:routes — routes are auto-injected above this line
24
+
25
+ // ─── Health Check ─────────────────────────────────────────────────────────────
26
+ app.get('/health', (c) => c.json({ status: 'ok', timestamp: Date.now() }));
27
+
28
+ export default app;
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": [
7
+ "ES2022"
8
+ ],
9
+ "types": [
10
+ "@cloudflare/workers-types"
11
+ ],
12
+ "strict": true,
13
+ "noEmit": true,
14
+ "allowImportingTsExtensions": true,
15
+ "paths": {
16
+ "@/*": [
17
+ "./src/*"
18
+ ]
19
+ }
20
+ },
21
+ "include": [
22
+ "src",
23
+ "**/*.ts"
24
+ ],
25
+ "exclude": [
26
+ "node_modules",
27
+ "dist"
28
+ ]
29
+ }
@@ -0,0 +1,16 @@
1
+ name = "{{PROJECT_NAME}}"
2
+ main = "src/index.ts"
3
+ compatibility_date = "2025-01-01"
4
+ compatibility_flags = ["nodejs_compat"]
5
+
6
+ [[d1_databases]]
7
+ binding = "DB"
8
+ database_name = "{{PROJECT_NAME}}-db"
9
+ database_id = "YOUR_DATABASE_ID_HERE"
10
+
11
+ [[kv_namespaces]]
12
+ binding = "CACHE"
13
+ id = "YOUR_KV_ID_HERE"
14
+
15
+ [ai]
16
+ binding = "AI"