create-ekka-desktop-app 0.2.2

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 (96) hide show
  1. package/README.md +137 -0
  2. package/bin/cli.js +72 -0
  3. package/package.json +23 -0
  4. package/template/branding/app.json +6 -0
  5. package/template/branding/icon.icns +0 -0
  6. package/template/eslint.config.js +98 -0
  7. package/template/index.html +29 -0
  8. package/template/package.json +40 -0
  9. package/template/src/app/App.tsx +24 -0
  10. package/template/src/demo/DemoApp.tsx +260 -0
  11. package/template/src/demo/components/Banner.tsx +82 -0
  12. package/template/src/demo/components/EmptyState.tsx +61 -0
  13. package/template/src/demo/components/InfoPopover.tsx +171 -0
  14. package/template/src/demo/components/InfoTooltip.tsx +76 -0
  15. package/template/src/demo/components/LearnMore.tsx +98 -0
  16. package/template/src/demo/components/NodeCredentialsOnboarding.tsx +219 -0
  17. package/template/src/demo/components/SetupWizard.tsx +48 -0
  18. package/template/src/demo/components/StatusBadge.tsx +83 -0
  19. package/template/src/demo/components/index.ts +10 -0
  20. package/template/src/demo/hooks/index.ts +6 -0
  21. package/template/src/demo/hooks/useAuditEvents.ts +30 -0
  22. package/template/src/demo/layout/Shell.tsx +110 -0
  23. package/template/src/demo/layout/Sidebar.tsx +192 -0
  24. package/template/src/demo/pages/AuditLogPage.tsx +235 -0
  25. package/template/src/demo/pages/DocGenPage.tsx +874 -0
  26. package/template/src/demo/pages/HomeSetupPage.tsx +182 -0
  27. package/template/src/demo/pages/LoginPage.tsx +192 -0
  28. package/template/src/demo/pages/PathPermissionsPage.tsx +873 -0
  29. package/template/src/demo/pages/RunnerPage.tsx +445 -0
  30. package/template/src/demo/pages/SystemPage.tsx +557 -0
  31. package/template/src/demo/pages/VaultPage.tsx +805 -0
  32. package/template/src/ekka/__tests__/demo-backend.test.ts +187 -0
  33. package/template/src/ekka/audit/index.ts +7 -0
  34. package/template/src/ekka/audit/store.ts +68 -0
  35. package/template/src/ekka/audit/types.ts +22 -0
  36. package/template/src/ekka/auth/client.ts +212 -0
  37. package/template/src/ekka/auth/index.ts +30 -0
  38. package/template/src/ekka/auth/storage.ts +114 -0
  39. package/template/src/ekka/auth/types.ts +67 -0
  40. package/template/src/ekka/backend/demo.ts +151 -0
  41. package/template/src/ekka/backend/interface.ts +36 -0
  42. package/template/src/ekka/config.ts +48 -0
  43. package/template/src/ekka/constants.ts +143 -0
  44. package/template/src/ekka/errors.ts +54 -0
  45. package/template/src/ekka/index.ts +516 -0
  46. package/template/src/ekka/internal/backend.ts +156 -0
  47. package/template/src/ekka/internal/index.ts +7 -0
  48. package/template/src/ekka/ops/auth.ts +29 -0
  49. package/template/src/ekka/ops/debug.ts +68 -0
  50. package/template/src/ekka/ops/home.ts +101 -0
  51. package/template/src/ekka/ops/index.ts +16 -0
  52. package/template/src/ekka/ops/nodeCredentials.ts +131 -0
  53. package/template/src/ekka/ops/nodeSession.ts +145 -0
  54. package/template/src/ekka/ops/paths.ts +183 -0
  55. package/template/src/ekka/ops/runner.ts +86 -0
  56. package/template/src/ekka/ops/runtime.ts +31 -0
  57. package/template/src/ekka/ops/setup.ts +47 -0
  58. package/template/src/ekka/ops/vault.ts +459 -0
  59. package/template/src/ekka/ops/workflowRuns.ts +116 -0
  60. package/template/src/ekka/types.ts +82 -0
  61. package/template/src/ekka/utils/idempotency.ts +14 -0
  62. package/template/src/ekka/utils/index.ts +7 -0
  63. package/template/src/ekka/utils/time.ts +77 -0
  64. package/template/src/main.tsx +12 -0
  65. package/template/src/vite-env.d.ts +12 -0
  66. package/template/src-tauri/Cargo.toml +41 -0
  67. package/template/src-tauri/build.rs +3 -0
  68. package/template/src-tauri/capabilities/default.json +11 -0
  69. package/template/src-tauri/icons/icon.icns +0 -0
  70. package/template/src-tauri/icons/icon.png +0 -0
  71. package/template/src-tauri/resources/ekka-engine-bootstrap +0 -0
  72. package/template/src-tauri/src/bootstrap.rs +37 -0
  73. package/template/src-tauri/src/commands.rs +1215 -0
  74. package/template/src-tauri/src/device_secret.rs +111 -0
  75. package/template/src-tauri/src/engine_process.rs +538 -0
  76. package/template/src-tauri/src/grants.rs +129 -0
  77. package/template/src-tauri/src/handlers/home.rs +65 -0
  78. package/template/src-tauri/src/handlers/mod.rs +7 -0
  79. package/template/src-tauri/src/handlers/paths.rs +128 -0
  80. package/template/src-tauri/src/handlers/vault.rs +680 -0
  81. package/template/src-tauri/src/main.rs +243 -0
  82. package/template/src-tauri/src/node_auth.rs +858 -0
  83. package/template/src-tauri/src/node_credentials.rs +541 -0
  84. package/template/src-tauri/src/node_runner.rs +882 -0
  85. package/template/src-tauri/src/node_vault_crypto.rs +113 -0
  86. package/template/src-tauri/src/node_vault_store.rs +267 -0
  87. package/template/src-tauri/src/ops/auth.rs +50 -0
  88. package/template/src-tauri/src/ops/home.rs +251 -0
  89. package/template/src-tauri/src/ops/mod.rs +7 -0
  90. package/template/src-tauri/src/ops/runtime.rs +21 -0
  91. package/template/src-tauri/src/state.rs +639 -0
  92. package/template/src-tauri/src/types.rs +84 -0
  93. package/template/src-tauri/tauri.conf.json +41 -0
  94. package/template/tsconfig.json +26 -0
  95. package/template/tsconfig.tsbuildinfo +1 -0
  96. package/template/vite.config.ts +34 -0
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # create-ekka-desktop-app
2
+
3
+ Scaffold a new EKKA desktop app with one command. Zero config, batteries included.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx create-ekka-desktop-app my-app
9
+ cd my-app
10
+ npm install
11
+ npm run tauri:dev
12
+ ```
13
+
14
+ That's it. You now have a native desktop app running.
15
+
16
+ ## What You Get
17
+
18
+ ```
19
+ my-app/
20
+ ├── src/
21
+ │ ├── app/App.tsx # Your app (start here)
22
+ │ ├── demo/ # Demo UI (delete when ready)
23
+ │ └── ekka/ # EKKA SDK (do not modify)
24
+ ├── src-tauri/ # Tauri (Rust) shell
25
+ ├── branding/ # App name, icon, bundle ID
26
+ ├── package.json
27
+ └── vite.config.ts
28
+ ```
29
+
30
+ ## Development
31
+
32
+ ```bash
33
+ # Web browser (fast reload)
34
+ npm start
35
+
36
+ # Desktop window (native)
37
+ npm run tauri:dev
38
+ ```
39
+
40
+ ## Build
41
+
42
+ ```bash
43
+ # Create distributable .app
44
+ npm run tauri:build
45
+ ```
46
+
47
+ Output: `src-tauri/target/release/bundle/macos/<AppName>.app`
48
+
49
+ ## Customize Branding
50
+
51
+ Edit `branding/app.json`:
52
+
53
+ ```json
54
+ {
55
+ "name": "My App",
56
+ "bundleId": "com.mycompany.myapp",
57
+ "version": "1.0.0"
58
+ }
59
+ ```
60
+
61
+ Replace `branding/icon.icns` with your app icon.
62
+
63
+ ## Project Structure
64
+
65
+ | Path | Purpose |
66
+ |------|---------|
67
+ | `src/app/App.tsx` | **Your app code starts here** |
68
+ | `src/demo/` | Demo UI - delete when you're ready to build your own |
69
+ | `src/ekka/` | EKKA SDK - provides secure APIs (do not modify) |
70
+ | `src-tauri/` | Tauri shell - handles native window, builds .app |
71
+ | `branding/` | App name, icon, bundle ID |
72
+
73
+ ## EKKA SDK
74
+
75
+ The app includes the EKKA SDK at `src/ekka/`. It provides:
76
+
77
+ - **Secure key-value storage** - Data persists across sessions
78
+ - **Background work queues** - Run async tasks reliably
79
+ - **Policy enforcement** - All operations are auditable
80
+
81
+ ```tsx
82
+ import { ekka } from './ekka';
83
+
84
+ // Store data
85
+ await ekka.store.set('key', 'value');
86
+
87
+ // Retrieve data
88
+ const value = await ekka.store.get('key');
89
+
90
+ // Queue background work
91
+ await ekka.work.enqueue({ task: 'process', data: {...} });
92
+ ```
93
+
94
+ ## Demo Mode
95
+
96
+ The app runs in **demo mode** by default - all data is stored in memory. This lets you develop and test without any backend setup.
97
+
98
+ When you're ready for production:
99
+ 1. Build with the EKKA engine sidecar (via `ekka-desktop-build`)
100
+ 2. The SDK automatically connects to the real backend
101
+
102
+ ## Requirements
103
+
104
+ - Node.js 18+
105
+ - Rust (for Tauri builds)
106
+ - Xcode Command Line Tools (macOS)
107
+
108
+ ## Commands
109
+
110
+ | Command | Description |
111
+ |---------|-------------|
112
+ | `npm start` | Start dev server (web) |
113
+ | `npm run tauri:dev` | Start dev server (desktop) |
114
+ | `npm run tauri:build` | Build distributable app |
115
+ | `npm run lint` | Run ESLint |
116
+ | `npm run build` | Build frontend only |
117
+
118
+ ## FAQ
119
+
120
+ **Q: How do I change the app name?**
121
+ Edit `branding/app.json` and set the `name` field.
122
+
123
+ **Q: How do I change the app icon?**
124
+ Replace `branding/icon.icns` with your icon file.
125
+
126
+ **Q: How do I remove the demo UI?**
127
+ Delete `src/demo/` and update `src/main.tsx` to render your own component.
128
+
129
+ **Q: Where is my data stored in demo mode?**
130
+ In memory. It resets when you restart the app.
131
+
132
+ **Q: How do I connect to a real backend?**
133
+ Build with `ekka-desktop-build` which injects the EKKA engine sidecar.
134
+
135
+ ## License
136
+
137
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { resolve, join, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const templateDir = resolve(__dirname, '..', 'template');
9
+
10
+ const projectName = process.argv[2];
11
+
12
+ if (!projectName) {
13
+ console.error('Usage: npx create-ekka-desktop-app <project-name>');
14
+ process.exit(1);
15
+ }
16
+
17
+ const targetDir = resolve(process.cwd(), projectName);
18
+
19
+ if (existsSync(targetDir)) {
20
+ console.error(`Error: Directory "${projectName}" already exists.`);
21
+ process.exit(1);
22
+ }
23
+
24
+ console.log(`Creating EKKA desktop app in ${targetDir}...`);
25
+
26
+ // Copy template
27
+ mkdirSync(targetDir, { recursive: true });
28
+ cpSync(templateDir, targetDir, { recursive: true });
29
+
30
+ // Update package.json with project name
31
+ const pkgPath = join(targetDir, 'package.json');
32
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
33
+ pkg.name = projectName;
34
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
35
+
36
+ // Update branding/app.json with project name
37
+ const brandingPath = join(targetDir, 'branding', 'app.json');
38
+ const branding = JSON.parse(readFileSync(brandingPath, 'utf8'));
39
+ // Convert project-name to "Project Name" for display
40
+ const displayName = projectName
41
+ .split('-')
42
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
43
+ .join(' ');
44
+ // Convert project-name to ai.ekka.projectname for bundleId
45
+ const bundleId = `ai.ekka.${projectName.replace(/-/g, '')}`;
46
+ branding.name = displayName;
47
+ branding.bundleId = bundleId;
48
+ writeFileSync(brandingPath, JSON.stringify(branding, null, 2) + '\n');
49
+
50
+ // Update src-tauri/Cargo.toml crate name
51
+ const cargoPath = join(targetDir, 'src-tauri', 'Cargo.toml');
52
+ let cargoContent = readFileSync(cargoPath, 'utf8');
53
+ cargoContent = cargoContent.replace(/^name = ".*"$/m, `name = "${projectName}"`);
54
+ cargoContent = cargoContent.replace(/^description = ".*"$/m, `description = "${displayName}"`);
55
+ writeFileSync(cargoPath, cargoContent);
56
+
57
+ console.log(`
58
+ Done! To get started:
59
+
60
+ cd ${projectName}
61
+ npm install
62
+
63
+ Development:
64
+ npm start # Web (browser)
65
+ npm run tauri:dev # Desktop (native window)
66
+
67
+ Build:
68
+ npm run tauri:build # Create distributable app
69
+
70
+ Edit src/app/App.tsx to build your UI.
71
+ Delete src/demo/ when ready.
72
+ `);
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "create-ekka-desktop-app",
3
+ "version": "0.2.2",
4
+ "description": "Create an EKKA desktop app with built-in demo backend. No setup required.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-ekka-desktop-app": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "template"
12
+ ],
13
+ "keywords": [
14
+ "ekka",
15
+ "desktop",
16
+ "scaffold",
17
+ "generator"
18
+ ],
19
+ "license": "MIT",
20
+ "engines": {
21
+ "node": ">=18.0.0"
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "EKKA Desktop",
3
+ "bundleId": "ai.ekka.desktop",
4
+ "version": "0.2.0",
5
+ "iconIcns": "branding/icon.icns"
6
+ }
Binary file
@@ -0,0 +1,98 @@
1
+ /**
2
+ * EKKA Desktop App ESLint Configuration
3
+ *
4
+ * GUARDRAILS ENFORCED:
5
+ * - No fs, https, axios, child_process imports (TS is sandboxed)
6
+ * - No process.env access (TS MUST NOT decide config)
7
+ * - No direct fetch() calls outside src/ekka/client.ts
8
+ * - All network MUST go through the ekka client
9
+ *
10
+ * These rules enforce the EKKA security model:
11
+ * TS communicates ONLY through the ekka client to EKKA's servers.
12
+ */
13
+
14
+ import js from '@eslint/js'
15
+ import globals from 'globals'
16
+ import reactHooks from 'eslint-plugin-react-hooks'
17
+ import reactRefresh from 'eslint-plugin-react-refresh'
18
+ import tseslint from 'typescript-eslint'
19
+ import { defineConfig, globalIgnores } from 'eslint/config'
20
+
21
+ export default defineConfig([
22
+ globalIgnores(['dist', 'node_modules', 'src-tauri', 'vite.config.ts', 'eslint.config.js']),
23
+
24
+ // Base config for all TypeScript files
25
+ {
26
+ files: ['**/*.{ts,tsx}'],
27
+ extends: [
28
+ js.configs.recommended,
29
+ tseslint.configs.recommended,
30
+ reactRefresh.configs.vite,
31
+ ],
32
+ plugins: {
33
+ 'react-hooks': reactHooks,
34
+ },
35
+ languageOptions: {
36
+ ecmaVersion: 2020,
37
+ globals: globals.browser,
38
+ },
39
+ rules: {
40
+ // React hooks rules
41
+ 'react-hooks/rules-of-hooks': 'error',
42
+ 'react-hooks/exhaustive-deps': 'warn',
43
+
44
+ // ========================================
45
+ // EKKA GUARDRAILS - FORBIDDEN IMPORTS
46
+ // ========================================
47
+ 'no-restricted-imports': ['error', {
48
+ paths: [
49
+ // Node.js modules - TS MUST NOT access filesystem or spawn processes
50
+ { name: 'fs', message: 'EKKA: TS is sandboxed. TS MUST NOT read files.' },
51
+ { name: 'fs/promises', message: 'EKKA: TS is sandboxed. TS MUST NOT read files.' },
52
+ { name: 'node:fs', message: 'EKKA: TS is sandboxed. TS MUST NOT read files.' },
53
+ { name: 'node:fs/promises', message: 'EKKA: TS is sandboxed. TS MUST NOT read files.' },
54
+ { name: 'path', message: 'EKKA: TS is sandboxed. Use ekka client for all operations.' },
55
+ { name: 'node:path', message: 'EKKA: TS is sandboxed. Use ekka client for all operations.' },
56
+ { name: 'child_process', message: 'EKKA: TS is sandboxed. TS MUST NOT spawn processes.' },
57
+ { name: 'node:child_process', message: 'EKKA: TS is sandboxed. TS MUST NOT spawn processes.' },
58
+
59
+ // Network libraries - TS MUST NOT make direct network calls
60
+ { name: 'https', message: 'EKKA: TS MUST NOT make direct network calls. Use ekka client only.' },
61
+ { name: 'node:https', message: 'EKKA: TS MUST NOT make direct network calls. Use ekka client only.' },
62
+ { name: 'http', message: 'EKKA: TS MUST NOT make direct network calls. Use ekka client only.' },
63
+ { name: 'node:http', message: 'EKKA: TS MUST NOT make direct network calls. Use ekka client only.' },
64
+ { name: 'axios', message: 'EKKA: TS MUST NOT use axios. Use ekka client only.' },
65
+ { name: 'node-fetch', message: 'EKKA: TS MUST NOT use node-fetch. Use ekka client only.' },
66
+
67
+ // Crypto - TS MUST NOT do crypto
68
+ { name: 'crypto', message: 'EKKA: TS MUST NOT do crypto. Server handles all crypto.' },
69
+ { name: 'node:crypto', message: 'EKKA: TS MUST NOT do crypto. Server handles all crypto.' },
70
+ ],
71
+ patterns: [
72
+ { group: ['axios/*'], message: 'EKKA: TS MUST NOT use axios. Use ekka client only.' },
73
+ ],
74
+ }],
75
+
76
+ // ========================================
77
+ // EKKA GUARDRAILS - NO PROCESS.ENV
78
+ // ========================================
79
+ 'no-restricted-globals': ['error',
80
+ { name: 'process', message: 'EKKA: TS MUST NOT access process.env. Config is managed by EKKA.' },
81
+ ],
82
+ },
83
+ },
84
+
85
+ // ========================================
86
+ // EKKA GUARDRAILS - NO FETCH() ANYWHERE
87
+ // ALL HTTP must go through Tauri/Rust via engine_request
88
+ // ========================================
89
+ {
90
+ files: ['src/**/*.{ts,tsx}'],
91
+ rules: {
92
+ 'no-restricted-globals': ['error',
93
+ { name: 'process', message: 'EKKA: TS MUST NOT access process.env. Config is managed by EKKA.' },
94
+ { name: 'fetch', message: 'EKKA: Direct fetch() is FORBIDDEN. All HTTP must go through Tauri via engine_request. See RULEBOOK_ARCHITECTURE_AI.md.' },
95
+ ],
96
+ },
97
+ },
98
+ ])
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>EKKA Desktop App</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+ html, body, #root {
14
+ height: 100%;
15
+ width: 100%;
16
+ overflow: hidden;
17
+ }
18
+ body {
19
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
20
+ -webkit-font-smoothing: antialiased;
21
+ -moz-osx-font-smoothing: grayscale;
22
+ }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <div id="root"></div>
27
+ <script type="module" src="/src/main.tsx"></script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "ekka-desktop-app",
3
+ "private": true,
4
+ "version": "0.2.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "start": "vite",
9
+ "build": "tsc -b && vite build",
10
+ "lint": "eslint .",
11
+ "lint:fix": "eslint . --fix",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest",
14
+ "preview": "vite preview",
15
+ "tauri": "tauri",
16
+ "tauri:dev": "tauri dev",
17
+ "tauri:build": "tauri build"
18
+ },
19
+ "dependencies": {
20
+ "@tauri-apps/api": "^2.0.0",
21
+ "@tauri-apps/plugin-dialog": "^2.6.0",
22
+ "react": "^19.0.0",
23
+ "react-dom": "^19.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@eslint/js": "^9.17.0",
27
+ "@tauri-apps/cli": "^2.0.0",
28
+ "@types/react": "^19.0.0",
29
+ "@types/react-dom": "^19.0.0",
30
+ "@vitejs/plugin-react": "^4.3.4",
31
+ "eslint": "^9.17.0",
32
+ "eslint-plugin-react-hooks": "^5.0.0",
33
+ "eslint-plugin-react-refresh": "^0.4.16",
34
+ "globals": "^15.14.0",
35
+ "typescript": "~5.6.2",
36
+ "typescript-eslint": "^8.18.2",
37
+ "vite": "^6.0.0",
38
+ "vitest": "^3.2.4"
39
+ }
40
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Your App - Edit this file!
3
+ *
4
+ * This is where you build your UI.
5
+ * Use the ekka client for all data operations.
6
+ */
7
+
8
+ import { ekka } from '../ekka';
9
+
10
+ export function App() {
11
+ // Example: Initialize on button click
12
+ async function handleStart() {
13
+ await ekka.connect();
14
+ // Now you can use ekka.db and ekka.queue
15
+ }
16
+
17
+ return (
18
+ <div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif' }}>
19
+ <h1>My EKKA App</h1>
20
+ <p>Edit <code>src/app/App.tsx</code> to get started.</p>
21
+ <button onClick={handleStart}>Start</button>
22
+ </div>
23
+ );
24
+ }