@lumerahq/cli 0.7.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.
Files changed (37) hide show
  1. package/README.md +118 -0
  2. package/dist/auth-7RGL7GXU.js +311 -0
  3. package/dist/chunk-2CR762KB.js +18 -0
  4. package/dist/chunk-AVKPM7C4.js +199 -0
  5. package/dist/chunk-D2BLSEGR.js +59 -0
  6. package/dist/chunk-NDLYGKS6.js +77 -0
  7. package/dist/chunk-V2XXMMEI.js +147 -0
  8. package/dist/dev-UTZC4ZJ7.js +87 -0
  9. package/dist/index.js +157 -0
  10. package/dist/init-OQCIET53.js +363 -0
  11. package/dist/migrate-2DZ6RQ5K.js +190 -0
  12. package/dist/resources-PNK3NESI.js +1350 -0
  13. package/dist/run-4NDI2CN4.js +257 -0
  14. package/dist/skills-56EUKHGY.js +414 -0
  15. package/dist/status-BEVUV6RY.js +131 -0
  16. package/package.json +37 -0
  17. package/templates/default/CLAUDE.md +245 -0
  18. package/templates/default/README.md +59 -0
  19. package/templates/default/biome.json +33 -0
  20. package/templates/default/index.html +13 -0
  21. package/templates/default/package.json.hbs +46 -0
  22. package/templates/default/platform/automations/.gitkeep +0 -0
  23. package/templates/default/platform/collections/example_items.json +28 -0
  24. package/templates/default/platform/hooks/.gitkeep +0 -0
  25. package/templates/default/pyproject.toml.hbs +14 -0
  26. package/templates/default/scripts/seed-demo.py +35 -0
  27. package/templates/default/src/components/Sidebar.tsx +84 -0
  28. package/templates/default/src/components/StatCard.tsx +31 -0
  29. package/templates/default/src/components/layout.tsx +13 -0
  30. package/templates/default/src/lib/queries.ts +27 -0
  31. package/templates/default/src/main.tsx +137 -0
  32. package/templates/default/src/routes/__root.tsx +10 -0
  33. package/templates/default/src/routes/index.tsx +90 -0
  34. package/templates/default/src/routes/settings.tsx +25 -0
  35. package/templates/default/src/styles.css +40 -0
  36. package/templates/default/tsconfig.json +23 -0
  37. package/templates/default/vite.config.ts +27 -0
@@ -0,0 +1,77 @@
1
+ // src/lib/auth.ts
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ var GLOBAL_CONFIG_DIR = join(homedir(), ".config", "lumera");
6
+ var GLOBAL_CREDS_PATH = join(GLOBAL_CONFIG_DIR, "credentials.json");
7
+ function getLocalCredsPath(cwd = process.cwd()) {
8
+ return join(cwd, ".lumera", "credentials.json");
9
+ }
10
+ function getToken(cwd = process.cwd()) {
11
+ if (process.env.LUMERA_TOKEN) {
12
+ return process.env.LUMERA_TOKEN;
13
+ }
14
+ const localCredsPath = getLocalCredsPath(cwd);
15
+ if (existsSync(localCredsPath)) {
16
+ try {
17
+ const creds = JSON.parse(readFileSync(localCredsPath, "utf-8"));
18
+ if (creds.token) return creds.token;
19
+ } catch {
20
+ }
21
+ }
22
+ if (existsSync(GLOBAL_CREDS_PATH)) {
23
+ try {
24
+ const creds = JSON.parse(readFileSync(GLOBAL_CREDS_PATH, "utf-8"));
25
+ if (creds.token) return creds.token;
26
+ } catch {
27
+ }
28
+ }
29
+ throw new Error(
30
+ "No Lumera token found.\n Run `lumera login` to authenticate, or\n Set LUMERA_TOKEN environment variable"
31
+ );
32
+ }
33
+ function getTokenSource(cwd = process.cwd()) {
34
+ if (process.env.LUMERA_TOKEN) {
35
+ return "environment (LUMERA_TOKEN)";
36
+ }
37
+ const localCredsPath = getLocalCredsPath(cwd);
38
+ if (existsSync(localCredsPath)) {
39
+ try {
40
+ const creds = JSON.parse(readFileSync(localCredsPath, "utf-8"));
41
+ if (creds.token) return `project (${localCredsPath})`;
42
+ } catch {
43
+ }
44
+ }
45
+ if (existsSync(GLOBAL_CREDS_PATH)) {
46
+ try {
47
+ const creds = JSON.parse(readFileSync(GLOBAL_CREDS_PATH, "utf-8"));
48
+ if (creds.token) return `global (${GLOBAL_CREDS_PATH})`;
49
+ } catch {
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ function getCredentials(cwd = process.cwd()) {
55
+ const localCredsPath = getLocalCredsPath(cwd);
56
+ if (existsSync(localCredsPath)) {
57
+ try {
58
+ const creds = JSON.parse(readFileSync(localCredsPath, "utf-8"));
59
+ if (creds.token) return creds;
60
+ } catch {
61
+ }
62
+ }
63
+ if (existsSync(GLOBAL_CREDS_PATH)) {
64
+ try {
65
+ const creds = JSON.parse(readFileSync(GLOBAL_CREDS_PATH, "utf-8"));
66
+ if (creds.token) return creds;
67
+ } catch {
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+
73
+ export {
74
+ getToken,
75
+ getTokenSource,
76
+ getCredentials
77
+ };
@@ -0,0 +1,147 @@
1
+ import {
2
+ getToken
3
+ } from "./chunk-NDLYGKS6.js";
4
+
5
+ // src/lib/api.ts
6
+ var ApiClient = class {
7
+ baseUrl;
8
+ token;
9
+ constructor(token, baseUrl) {
10
+ this.token = token || getToken();
11
+ let base = baseUrl || process.env.LUMERA_BASE_URL || "https://app.lumerahq.com";
12
+ base = base.replace(/\/$/, "");
13
+ if (base.endsWith("/api")) {
14
+ base = base.slice(0, -4);
15
+ }
16
+ this.baseUrl = base.replace(/\/$/, "");
17
+ }
18
+ async request(path, options = {}) {
19
+ const url = `${this.baseUrl}${path}`;
20
+ const headers = {
21
+ "Content-Type": "application/json",
22
+ Authorization: `Bearer ${this.token}`,
23
+ ...options.headers
24
+ };
25
+ const response = await fetch(url, {
26
+ ...options,
27
+ headers
28
+ });
29
+ if (!response.ok) {
30
+ const text = await response.text();
31
+ throw new Error(`API request failed: ${response.status} ${text}`);
32
+ }
33
+ if (response.status === 204) {
34
+ return {};
35
+ }
36
+ return response.json();
37
+ }
38
+ // Collections
39
+ async listCollections() {
40
+ const result = await this.request("/api/pb/collections");
41
+ return result.items;
42
+ }
43
+ async getCollection(id) {
44
+ return this.request(`/api/pb/collections/${id}`);
45
+ }
46
+ async ensureCollection(name, def) {
47
+ return this.request(`/api/pb/collections/${name}`, {
48
+ method: "PUT",
49
+ body: JSON.stringify(def)
50
+ });
51
+ }
52
+ async deleteCollection(id) {
53
+ await this.request(`/api/pb/collections/${id}`, {
54
+ method: "DELETE"
55
+ });
56
+ }
57
+ // Automations
58
+ async listAutomations(params) {
59
+ let path = "/api/automations";
60
+ if (params?.external_id) {
61
+ path += `?external_id=${encodeURIComponent(params.external_id)}`;
62
+ }
63
+ const result = await this.request(path);
64
+ return result.automations || [];
65
+ }
66
+ async getAutomation(id) {
67
+ return this.request(`/api/automations/${id}`);
68
+ }
69
+ async createAutomation(def) {
70
+ return this.request("/api/automations", {
71
+ method: "POST",
72
+ body: JSON.stringify(def)
73
+ });
74
+ }
75
+ async updateAutomation(id, def) {
76
+ return this.request(`/api/automations/${id}`, {
77
+ method: "PATCH",
78
+ body: JSON.stringify(def)
79
+ });
80
+ }
81
+ async deleteAutomation(id) {
82
+ await this.request(`/api/automations/${id}`, {
83
+ method: "DELETE"
84
+ });
85
+ }
86
+ // Automation Presets
87
+ async listPresets(automationId) {
88
+ const result = await this.request(
89
+ `/api/automations/${automationId}/presets`
90
+ );
91
+ return result.presets || [];
92
+ }
93
+ async createPreset(automationId, def) {
94
+ return this.request(`/api/automations/${automationId}/presets`, {
95
+ method: "POST",
96
+ body: JSON.stringify(def)
97
+ });
98
+ }
99
+ async updatePreset(presetId, def) {
100
+ return this.request(`/api/automation-presets/${presetId}`, {
101
+ method: "PATCH",
102
+ body: JSON.stringify(def)
103
+ });
104
+ }
105
+ async deletePreset(presetId) {
106
+ await this.request(`/api/automation-presets/${presetId}`, {
107
+ method: "DELETE"
108
+ });
109
+ }
110
+ // Hooks
111
+ async listHooks(params) {
112
+ const query = new URLSearchParams();
113
+ if (params?.collection_id) query.set("collection_id", params.collection_id);
114
+ if (params?.external_id) query.set("external_id", params.external_id);
115
+ const queryString = query.toString();
116
+ const path = queryString ? `/api/pb/hooks?${queryString}` : "/api/pb/hooks";
117
+ const result = await this.request(path);
118
+ return result.items || [];
119
+ }
120
+ async getHook(id) {
121
+ return this.request(`/api/pb/hooks/${id}`);
122
+ }
123
+ async createHook(def) {
124
+ return this.request("/api/pb/hooks", {
125
+ method: "POST",
126
+ body: JSON.stringify(def)
127
+ });
128
+ }
129
+ async updateHook(id, def) {
130
+ return this.request(`/api/pb/hooks/${id}`, {
131
+ method: "PATCH",
132
+ body: JSON.stringify(def)
133
+ });
134
+ }
135
+ async deleteHook(id) {
136
+ await this.request(`/api/pb/hooks/${id}`, {
137
+ method: "DELETE"
138
+ });
139
+ }
140
+ };
141
+ function createApiClient(token, baseUrl) {
142
+ return new ApiClient(token, baseUrl);
143
+ }
144
+
145
+ export {
146
+ createApiClient
147
+ };
@@ -0,0 +1,87 @@
1
+ import {
2
+ dev
3
+ } from "./chunk-AVKPM7C4.js";
4
+ import {
5
+ loadEnv
6
+ } from "./chunk-2CR762KB.js";
7
+ import {
8
+ getToken
9
+ } from "./chunk-NDLYGKS6.js";
10
+ import {
11
+ findProjectRoot,
12
+ getApiUrl,
13
+ getAppName,
14
+ getAppTitle
15
+ } from "./chunk-D2BLSEGR.js";
16
+
17
+ // src/commands/dev.ts
18
+ import pc from "picocolors";
19
+ function parseFlags(args) {
20
+ const result = {};
21
+ for (let i = 0; i < args.length; i++) {
22
+ const arg = args[i];
23
+ if (arg.startsWith("--")) {
24
+ const key = arg.slice(2);
25
+ const next = args[i + 1];
26
+ if (next && !next.startsWith("--")) {
27
+ result[key] = next;
28
+ i++;
29
+ } else {
30
+ result[key] = true;
31
+ }
32
+ }
33
+ }
34
+ return result;
35
+ }
36
+ function showHelp() {
37
+ console.log(`
38
+ ${pc.dim("Usage:")}
39
+ lumera dev [options]
40
+
41
+ ${pc.dim("Description:")}
42
+ Start the development server with Lumera registration.
43
+ Registers your app with Lumera and starts a local dev server.
44
+
45
+ ${pc.dim("Options:")}
46
+ --port <number> Dev server port (default: 8080)
47
+ --url <url> App URL for dev mode (default: http://localhost:{port})
48
+ --help, -h Show this help
49
+
50
+ ${pc.dim("Environment variables:")}
51
+ LUMERA_TOKEN API token (overrides \`lumera login\`)
52
+ LUMERA_API_URL API base URL (default: https://app.lumerahq.com/api)
53
+ PORT Dev server port (default: 8080)
54
+ APP_URL App URL for dev mode
55
+
56
+ ${pc.dim("Examples:")}
57
+ lumera dev # Start dev server on port 8080
58
+ lumera dev --port 3000 # Start dev server on port 3000
59
+ lumera dev --url http://192.168.1.100:8080 # Custom URL for mobile testing
60
+ `);
61
+ }
62
+ async function dev2(args) {
63
+ if (args.includes("--help") || args.includes("-h")) {
64
+ showHelp();
65
+ return;
66
+ }
67
+ const flags = parseFlags(args);
68
+ const projectRoot = findProjectRoot();
69
+ loadEnv(projectRoot);
70
+ const token = getToken(projectRoot);
71
+ const appName = getAppName(projectRoot);
72
+ const appTitle = getAppTitle(projectRoot);
73
+ const apiUrl = getApiUrl();
74
+ const port = Number(flags.port || process.env.PORT || "8080");
75
+ const appUrl = typeof flags.url === "string" ? flags.url : process.env.APP_URL;
76
+ await dev({
77
+ token,
78
+ appName,
79
+ appTitle,
80
+ port,
81
+ appUrl,
82
+ apiUrl
83
+ });
84
+ }
85
+ export {
86
+ dev2 as dev
87
+ };
package/dist/index.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { readFileSync } from "fs";
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import pc from "picocolors";
8
+ var __dirname = dirname(fileURLToPath(import.meta.url));
9
+ var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
10
+ var VERSION = pkg.version;
11
+ var args = process.argv.slice(2);
12
+ var command = args[0];
13
+ var subcommand = args[1];
14
+ function showHelp() {
15
+ console.log(`
16
+ ${pc.bold("Lumera CLI")} - Build and deploy Lumera apps
17
+
18
+ ${pc.dim("Usage:")}
19
+ lumera <command> [options]
20
+
21
+ ${pc.dim("Resource Commands:")}
22
+ ${pc.cyan("plan")} [resource] Preview changes
23
+ ${pc.cyan("apply")} [resource] Create/update resources
24
+ ${pc.cyan("pull")} [resource] Download remote state
25
+ ${pc.cyan("destroy")} [resource] Delete resources
26
+ ${pc.cyan("list")} [type] List resources with status
27
+ ${pc.cyan("show")} <resource> Show resource details
28
+
29
+ ${pc.dim("Development:")}
30
+ ${pc.cyan("dev")} Start dev server
31
+ ${pc.cyan("run")} <target> Run script or trigger automation
32
+
33
+ ${pc.dim("Project:")}
34
+ ${pc.cyan("init")} [name] Scaffold a new project
35
+ ${pc.cyan("status")} Show project info
36
+ ${pc.cyan("migrate")} Upgrade legacy project
37
+
38
+ ${pc.dim("Skills:")}
39
+ ${pc.cyan("skills list")} List available skills
40
+ ${pc.cyan("skills install")} Install skills
41
+ ${pc.cyan("skills update")} Update skills
42
+
43
+ ${pc.dim("Auth:")}
44
+ ${pc.cyan("login")} Login to Lumera
45
+ ${pc.cyan("logout")} Clear credentials
46
+ ${pc.cyan("whoami")} Show current user
47
+
48
+ ${pc.dim("Options:")}
49
+ --help, -h Show help
50
+ --version, -v Show version
51
+
52
+ ${pc.dim("Resource Types:")}
53
+ collections Data collections (schemas)
54
+ automations Background Python scripts
55
+ hooks Collection lifecycle handlers
56
+ app Frontend application
57
+
58
+ ${pc.dim("Examples:")}
59
+ lumera apply # Apply all resources
60
+ lumera apply collections/users # Apply single collection
61
+ lumera apply app # Deploy frontend
62
+ lumera list # List all resources
63
+ lumera show collections/users # Show collection details
64
+ lumera run scripts/seed.py # Run a script
65
+ lumera run automations/sync # Trigger automation
66
+ lumera dev # Start dev server
67
+
68
+ ${pc.dim("Documentation:")}
69
+ https://docs.lumerahq.com/cli
70
+ `);
71
+ }
72
+ function showVersion() {
73
+ console.log(`lumera ${VERSION}`);
74
+ }
75
+ async function main() {
76
+ if (!command || command.startsWith("-")) {
77
+ if (args.includes("--help") || args.includes("-h") || args.length === 0) {
78
+ showHelp();
79
+ process.exit(0);
80
+ }
81
+ if (args.includes("--version") || args.includes("-v")) {
82
+ showVersion();
83
+ process.exit(0);
84
+ }
85
+ if (command?.startsWith("-")) {
86
+ console.error(pc.red(`Unknown option: ${command}`));
87
+ showHelp();
88
+ process.exit(1);
89
+ }
90
+ }
91
+ try {
92
+ switch (command) {
93
+ // Resource commands
94
+ case "plan":
95
+ await import("./resources-PNK3NESI.js").then((m) => m.plan(args.slice(1)));
96
+ break;
97
+ case "apply":
98
+ await import("./resources-PNK3NESI.js").then((m) => m.apply(args.slice(1)));
99
+ break;
100
+ case "pull":
101
+ await import("./resources-PNK3NESI.js").then((m) => m.pull(args.slice(1)));
102
+ break;
103
+ case "destroy":
104
+ await import("./resources-PNK3NESI.js").then((m) => m.destroy(args.slice(1)));
105
+ break;
106
+ case "list":
107
+ await import("./resources-PNK3NESI.js").then((m) => m.list(args.slice(1)));
108
+ break;
109
+ case "show":
110
+ await import("./resources-PNK3NESI.js").then((m) => m.show(args.slice(1)));
111
+ break;
112
+ // Development
113
+ case "dev":
114
+ await import("./dev-UTZC4ZJ7.js").then((m) => m.dev(args.slice(1)));
115
+ break;
116
+ case "run":
117
+ await import("./run-4NDI2CN4.js").then((m) => m.run(args.slice(1)));
118
+ break;
119
+ // Project
120
+ case "init":
121
+ await import("./init-OQCIET53.js").then((m) => m.init(args.slice(1)));
122
+ break;
123
+ case "status":
124
+ await import("./status-BEVUV6RY.js").then((m) => m.status(args.slice(1)));
125
+ break;
126
+ case "migrate":
127
+ await import("./migrate-2DZ6RQ5K.js").then((m) => m.migrate(args.slice(1)));
128
+ break;
129
+ // Skills
130
+ case "skills":
131
+ await import("./skills-56EUKHGY.js").then((m) => m.skills(subcommand, args.slice(2)));
132
+ break;
133
+ // Auth
134
+ case "login":
135
+ await import("./auth-7RGL7GXU.js").then((m) => m.login(args.slice(1)));
136
+ break;
137
+ case "logout":
138
+ await import("./auth-7RGL7GXU.js").then((m) => m.logout(args.slice(1)));
139
+ break;
140
+ case "whoami":
141
+ await import("./auth-7RGL7GXU.js").then((m) => m.whoami());
142
+ break;
143
+ default:
144
+ console.error(pc.red(`Unknown command: ${command}`));
145
+ console.error(`Run ${pc.cyan("lumera --help")} for usage.`);
146
+ process.exit(1);
147
+ }
148
+ } catch (error) {
149
+ if (error instanceof Error) {
150
+ console.error(pc.red(`Error: ${error.message}`));
151
+ } else {
152
+ console.error(pc.red("An unexpected error occurred"));
153
+ }
154
+ process.exit(1);
155
+ }
156
+ }
157
+ main();