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.
- package/README.md +137 -0
- package/bin/cli.js +72 -0
- package/package.json +23 -0
- package/template/branding/app.json +6 -0
- package/template/branding/icon.icns +0 -0
- package/template/eslint.config.js +98 -0
- package/template/index.html +29 -0
- package/template/package.json +40 -0
- package/template/src/app/App.tsx +24 -0
- package/template/src/demo/DemoApp.tsx +260 -0
- package/template/src/demo/components/Banner.tsx +82 -0
- package/template/src/demo/components/EmptyState.tsx +61 -0
- package/template/src/demo/components/InfoPopover.tsx +171 -0
- package/template/src/demo/components/InfoTooltip.tsx +76 -0
- package/template/src/demo/components/LearnMore.tsx +98 -0
- package/template/src/demo/components/NodeCredentialsOnboarding.tsx +219 -0
- package/template/src/demo/components/SetupWizard.tsx +48 -0
- package/template/src/demo/components/StatusBadge.tsx +83 -0
- package/template/src/demo/components/index.ts +10 -0
- package/template/src/demo/hooks/index.ts +6 -0
- package/template/src/demo/hooks/useAuditEvents.ts +30 -0
- package/template/src/demo/layout/Shell.tsx +110 -0
- package/template/src/demo/layout/Sidebar.tsx +192 -0
- package/template/src/demo/pages/AuditLogPage.tsx +235 -0
- package/template/src/demo/pages/DocGenPage.tsx +874 -0
- package/template/src/demo/pages/HomeSetupPage.tsx +182 -0
- package/template/src/demo/pages/LoginPage.tsx +192 -0
- package/template/src/demo/pages/PathPermissionsPage.tsx +873 -0
- package/template/src/demo/pages/RunnerPage.tsx +445 -0
- package/template/src/demo/pages/SystemPage.tsx +557 -0
- package/template/src/demo/pages/VaultPage.tsx +805 -0
- package/template/src/ekka/__tests__/demo-backend.test.ts +187 -0
- package/template/src/ekka/audit/index.ts +7 -0
- package/template/src/ekka/audit/store.ts +68 -0
- package/template/src/ekka/audit/types.ts +22 -0
- package/template/src/ekka/auth/client.ts +212 -0
- package/template/src/ekka/auth/index.ts +30 -0
- package/template/src/ekka/auth/storage.ts +114 -0
- package/template/src/ekka/auth/types.ts +67 -0
- package/template/src/ekka/backend/demo.ts +151 -0
- package/template/src/ekka/backend/interface.ts +36 -0
- package/template/src/ekka/config.ts +48 -0
- package/template/src/ekka/constants.ts +143 -0
- package/template/src/ekka/errors.ts +54 -0
- package/template/src/ekka/index.ts +516 -0
- package/template/src/ekka/internal/backend.ts +156 -0
- package/template/src/ekka/internal/index.ts +7 -0
- package/template/src/ekka/ops/auth.ts +29 -0
- package/template/src/ekka/ops/debug.ts +68 -0
- package/template/src/ekka/ops/home.ts +101 -0
- package/template/src/ekka/ops/index.ts +16 -0
- package/template/src/ekka/ops/nodeCredentials.ts +131 -0
- package/template/src/ekka/ops/nodeSession.ts +145 -0
- package/template/src/ekka/ops/paths.ts +183 -0
- package/template/src/ekka/ops/runner.ts +86 -0
- package/template/src/ekka/ops/runtime.ts +31 -0
- package/template/src/ekka/ops/setup.ts +47 -0
- package/template/src/ekka/ops/vault.ts +459 -0
- package/template/src/ekka/ops/workflowRuns.ts +116 -0
- package/template/src/ekka/types.ts +82 -0
- package/template/src/ekka/utils/idempotency.ts +14 -0
- package/template/src/ekka/utils/index.ts +7 -0
- package/template/src/ekka/utils/time.ts +77 -0
- package/template/src/main.tsx +12 -0
- package/template/src/vite-env.d.ts +12 -0
- package/template/src-tauri/Cargo.toml +41 -0
- package/template/src-tauri/build.rs +3 -0
- package/template/src-tauri/capabilities/default.json +11 -0
- package/template/src-tauri/icons/icon.icns +0 -0
- package/template/src-tauri/icons/icon.png +0 -0
- package/template/src-tauri/resources/ekka-engine-bootstrap +0 -0
- package/template/src-tauri/src/bootstrap.rs +37 -0
- package/template/src-tauri/src/commands.rs +1215 -0
- package/template/src-tauri/src/device_secret.rs +111 -0
- package/template/src-tauri/src/engine_process.rs +538 -0
- package/template/src-tauri/src/grants.rs +129 -0
- package/template/src-tauri/src/handlers/home.rs +65 -0
- package/template/src-tauri/src/handlers/mod.rs +7 -0
- package/template/src-tauri/src/handlers/paths.rs +128 -0
- package/template/src-tauri/src/handlers/vault.rs +680 -0
- package/template/src-tauri/src/main.rs +243 -0
- package/template/src-tauri/src/node_auth.rs +858 -0
- package/template/src-tauri/src/node_credentials.rs +541 -0
- package/template/src-tauri/src/node_runner.rs +882 -0
- package/template/src-tauri/src/node_vault_crypto.rs +113 -0
- package/template/src-tauri/src/node_vault_store.rs +267 -0
- package/template/src-tauri/src/ops/auth.rs +50 -0
- package/template/src-tauri/src/ops/home.rs +251 -0
- package/template/src-tauri/src/ops/mod.rs +7 -0
- package/template/src-tauri/src/ops/runtime.rs +21 -0
- package/template/src-tauri/src/state.rs +639 -0
- package/template/src-tauri/src/types.rs +84 -0
- package/template/src-tauri/tauri.conf.json +41 -0
- package/template/tsconfig.json +26 -0
- package/template/tsconfig.tsbuildinfo +1 -0
- package/template/vite.config.ts +34 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EKKA Demo Backend
|
|
3
|
+
*
|
|
4
|
+
* In-memory implementation for development/testing.
|
|
5
|
+
* Only implements ops that have real Rust implementations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { EngineRequest, EngineResponse } from '../types';
|
|
9
|
+
import type { Backend } from './interface';
|
|
10
|
+
import { OPS, ERROR_CODES } from '../constants';
|
|
11
|
+
import { ok, err } from '../types';
|
|
12
|
+
import branding from '../../../branding/app.json';
|
|
13
|
+
|
|
14
|
+
// Derive display-only home path from branding (no OS detection, works in browser)
|
|
15
|
+
function slugify(name: string): string {
|
|
16
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
17
|
+
}
|
|
18
|
+
const DEMO_HOME_PATH = `~/.local/share/${slugify(branding.name) || 'ekka-desktop'}`;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Demo backend using in-memory storage.
|
|
22
|
+
* Used when Tauri engine is not available.
|
|
23
|
+
*/
|
|
24
|
+
export class DemoBackend implements Backend {
|
|
25
|
+
private connected = false;
|
|
26
|
+
private authContext: { tenantId: string; sub: string; jwt: string } | null = null;
|
|
27
|
+
private homeGranted = false;
|
|
28
|
+
|
|
29
|
+
async connect(): Promise<void> {
|
|
30
|
+
await Promise.resolve();
|
|
31
|
+
this.connected = true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
disconnect(): void {
|
|
35
|
+
this.connected = false;
|
|
36
|
+
this.authContext = null;
|
|
37
|
+
this.homeGranted = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isConnected(): boolean {
|
|
41
|
+
return this.connected;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async request(req: EngineRequest): Promise<EngineResponse> {
|
|
45
|
+
await Promise.resolve();
|
|
46
|
+
return this.handle(req);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private handle(req: EngineRequest): EngineResponse {
|
|
50
|
+
// Check connection for non-runtime ops
|
|
51
|
+
if (req.op !== OPS.RUNTIME_INFO && !this.connected) {
|
|
52
|
+
return err(ERROR_CODES.NOT_CONNECTED, 'Not connected. Call ekka.connect() first.');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
switch (req.op) {
|
|
56
|
+
// -----------------------------------------------------------------------
|
|
57
|
+
// Runtime
|
|
58
|
+
// -----------------------------------------------------------------------
|
|
59
|
+
case OPS.RUNTIME_INFO: {
|
|
60
|
+
return ok({
|
|
61
|
+
runtime: 'demo',
|
|
62
|
+
engine_present: false,
|
|
63
|
+
mode: 'demo',
|
|
64
|
+
homeState: this.getHomeState(),
|
|
65
|
+
homePath: DEMO_HOME_PATH,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// -----------------------------------------------------------------------
|
|
70
|
+
// Auth
|
|
71
|
+
// -----------------------------------------------------------------------
|
|
72
|
+
case OPS.AUTH_SET: {
|
|
73
|
+
const payload = req.payload as { tenantId?: string; sub?: string; jwt?: string };
|
|
74
|
+
|
|
75
|
+
if (!payload?.tenantId || !payload?.sub || !payload?.jwt) {
|
|
76
|
+
return err(ERROR_CODES.INVALID_PAYLOAD, 'Missing tenantId, sub, or jwt');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.authContext = {
|
|
80
|
+
tenantId: payload.tenantId,
|
|
81
|
+
sub: payload.sub,
|
|
82
|
+
jwt: payload.jwt,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return ok({ ok: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// -----------------------------------------------------------------------
|
|
89
|
+
// Home
|
|
90
|
+
// -----------------------------------------------------------------------
|
|
91
|
+
case OPS.HOME_STATUS: {
|
|
92
|
+
return ok({
|
|
93
|
+
state: this.getHomeState(),
|
|
94
|
+
homePath: DEMO_HOME_PATH,
|
|
95
|
+
grantPresent: this.homeGranted,
|
|
96
|
+
reason: this.homeGranted ? null : 'Demo mode - call home.grant to simulate',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case OPS.HOME_GRANT: {
|
|
101
|
+
if (!this.authContext) {
|
|
102
|
+
return err(ERROR_CODES.NOT_AUTHENTICATED, 'Must call auth.set before home.grant');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Simulate grant issuance
|
|
106
|
+
this.homeGranted = true;
|
|
107
|
+
|
|
108
|
+
return ok({
|
|
109
|
+
success: true,
|
|
110
|
+
grant_id: 'demo-grant-' + Date.now(),
|
|
111
|
+
expires_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// -----------------------------------------------------------------------
|
|
116
|
+
// Setup (pre-login) - only checks node credentials
|
|
117
|
+
// Home folder grant is post-login
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
case OPS.SETUP_STATUS: {
|
|
120
|
+
// In demo mode, node credentials are never configured
|
|
121
|
+
return ok({
|
|
122
|
+
nodeIdentity: 'not_configured',
|
|
123
|
+
setupComplete: false,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// -----------------------------------------------------------------------
|
|
128
|
+
// Unknown
|
|
129
|
+
// -----------------------------------------------------------------------
|
|
130
|
+
default:
|
|
131
|
+
return err(ERROR_CODES.INVALID_OP, `Unknown operation: ${req.op}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private getHomeState(): string {
|
|
136
|
+
if (this.homeGranted) return 'HOME_GRANTED';
|
|
137
|
+
if (this.authContext) return 'AUTHENTICATED_NO_HOME_GRANT';
|
|
138
|
+
return 'BOOTSTRAP_PRE_LOGIN';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Reset all state (for testing).
|
|
143
|
+
*/
|
|
144
|
+
resetAll(): void {
|
|
145
|
+
this.connected = false;
|
|
146
|
+
this.authContext = null;
|
|
147
|
+
this.homeGranted = false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const demoBackend = new DemoBackend();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EKKA Backend Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines the contract for all backend implementations.
|
|
5
|
+
* Both DemoBackend and EngineBackend implement this interface.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { EngineRequest, EngineResponse } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Backend interface for EKKA operations.
|
|
12
|
+
* All requests flow through a backend implementing this interface.
|
|
13
|
+
*/
|
|
14
|
+
export interface Backend {
|
|
15
|
+
/**
|
|
16
|
+
* Connect to the backend.
|
|
17
|
+
* Must be called before any db/queue operations.
|
|
18
|
+
*/
|
|
19
|
+
connect(): Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Disconnect from the backend.
|
|
23
|
+
*/
|
|
24
|
+
disconnect(): void;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if connected to the backend.
|
|
28
|
+
*/
|
|
29
|
+
isConnected(): boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Send a request to the backend and receive a response.
|
|
33
|
+
* Single RPC method for all operations.
|
|
34
|
+
*/
|
|
35
|
+
request(req: EngineRequest): Promise<EngineResponse>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EKKA Configuration
|
|
3
|
+
*
|
|
4
|
+
* API configuration and client identity constants.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// API CONFIGURATION
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
const getApiUrl = (): string => {
|
|
12
|
+
if (import.meta.env.PROD) return 'https://api.ekka.ai';
|
|
13
|
+
if (import.meta.env.VITE_EKKA_API_URL) return import.meta.env.VITE_EKKA_API_URL;
|
|
14
|
+
return 'https://api.ekka.ai';
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** API base URL */
|
|
18
|
+
export const API_BASE_URL = getApiUrl();
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// ENGINE CONFIGURATION
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
const getEngineUrl = (): string => {
|
|
25
|
+
if (import.meta.env.VITE_EKKA_ENGINE_URL) return import.meta.env.VITE_EKKA_ENGINE_URL;
|
|
26
|
+
return 'http://localhost:3200'; // Default local engine port
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Engine base URL for workflow runs */
|
|
30
|
+
export const ENGINE_BASE_URL = getEngineUrl();
|
|
31
|
+
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// CLIENT IDENTITY (Security Envelope)
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
export const CLIENT_TYPE = 'desktop';
|
|
37
|
+
export const CLIENT_VERSION = '0.2.0';
|
|
38
|
+
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// AUTH ENDPOINTS
|
|
41
|
+
// =============================================================================
|
|
42
|
+
|
|
43
|
+
export const AUTH_ENDPOINTS = {
|
|
44
|
+
login: '/auth/login',
|
|
45
|
+
refresh: '/auth/refresh',
|
|
46
|
+
logout: '/auth/logout',
|
|
47
|
+
me: '/auth/me',
|
|
48
|
+
} as const;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EKKA Constants
|
|
3
|
+
*
|
|
4
|
+
* Operation names and error codes.
|
|
5
|
+
* Only includes IMPLEMENTED operations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// OPERATION NAMES (only implemented ops)
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
export const OPS = {
|
|
13
|
+
// Setup
|
|
14
|
+
SETUP_STATUS: 'setup.status',
|
|
15
|
+
|
|
16
|
+
// Runtime
|
|
17
|
+
RUNTIME_INFO: 'runtime.info',
|
|
18
|
+
|
|
19
|
+
// Auth
|
|
20
|
+
AUTH_SET: 'auth.set',
|
|
21
|
+
|
|
22
|
+
// Node Session
|
|
23
|
+
NODE_SESSION_ENSURE_IDENTITY: 'nodeSession.ensureIdentity',
|
|
24
|
+
NODE_SESSION_BOOTSTRAP: 'nodeSession.bootstrap',
|
|
25
|
+
NODE_SESSION_STATUS: 'nodeSession.status',
|
|
26
|
+
|
|
27
|
+
// Node Credentials (keychain-stored)
|
|
28
|
+
NODE_CREDENTIALS_SET: 'nodeCredentials.set',
|
|
29
|
+
NODE_CREDENTIALS_STATUS: 'nodeCredentials.status',
|
|
30
|
+
NODE_CREDENTIALS_CLEAR: 'nodeCredentials.clear',
|
|
31
|
+
|
|
32
|
+
// Runner
|
|
33
|
+
RUNNER_STATUS: 'runner.status',
|
|
34
|
+
RUNNER_TASK_STATS: 'runner.taskStats',
|
|
35
|
+
|
|
36
|
+
// Workflow Runs (proxied via Rust)
|
|
37
|
+
WORKFLOW_RUNS_CREATE: 'workflowRuns.create',
|
|
38
|
+
WORKFLOW_RUNS_GET: 'workflowRuns.get',
|
|
39
|
+
|
|
40
|
+
// Auth (proxied via Rust)
|
|
41
|
+
AUTH_LOGIN: 'auth.login',
|
|
42
|
+
AUTH_REFRESH: 'auth.refresh',
|
|
43
|
+
AUTH_LOGOUT: 'auth.logout',
|
|
44
|
+
|
|
45
|
+
// Home
|
|
46
|
+
HOME_STATUS: 'home.status',
|
|
47
|
+
HOME_GRANT: 'home.grant',
|
|
48
|
+
|
|
49
|
+
// Paths
|
|
50
|
+
PATHS_CHECK: 'paths.check',
|
|
51
|
+
PATHS_LIST: 'paths.list',
|
|
52
|
+
PATHS_GET: 'paths.get',
|
|
53
|
+
PATHS_REQUEST: 'paths.request',
|
|
54
|
+
PATHS_REMOVE: 'paths.remove',
|
|
55
|
+
|
|
56
|
+
// Vault - Status/Capabilities
|
|
57
|
+
VAULT_STATUS: 'vault.status',
|
|
58
|
+
VAULT_CAPABILITIES: 'vault.capabilities',
|
|
59
|
+
|
|
60
|
+
// Vault - Secrets
|
|
61
|
+
VAULT_SECRETS_LIST: 'vault.secrets.list',
|
|
62
|
+
VAULT_SECRETS_GET: 'vault.secrets.get',
|
|
63
|
+
VAULT_SECRETS_CREATE: 'vault.secrets.create',
|
|
64
|
+
VAULT_SECRETS_UPDATE: 'vault.secrets.update',
|
|
65
|
+
VAULT_SECRETS_DELETE: 'vault.secrets.delete',
|
|
66
|
+
VAULT_SECRETS_UPSERT: 'vault.secrets.upsert',
|
|
67
|
+
|
|
68
|
+
// Vault - Bundles
|
|
69
|
+
VAULT_BUNDLES_LIST: 'vault.bundles.list',
|
|
70
|
+
VAULT_BUNDLES_GET: 'vault.bundles.get',
|
|
71
|
+
VAULT_BUNDLES_CREATE: 'vault.bundles.create',
|
|
72
|
+
VAULT_BUNDLES_RENAME: 'vault.bundles.rename',
|
|
73
|
+
VAULT_BUNDLES_DELETE: 'vault.bundles.delete',
|
|
74
|
+
VAULT_BUNDLES_LIST_SECRETS: 'vault.bundles.listSecrets',
|
|
75
|
+
VAULT_BUNDLES_ADD_SECRET: 'vault.bundles.addSecret',
|
|
76
|
+
VAULT_BUNDLES_REMOVE_SECRET: 'vault.bundles.removeSecret',
|
|
77
|
+
|
|
78
|
+
// Vault - Files
|
|
79
|
+
VAULT_FILES_WRITE_TEXT: 'vault.files.writeText',
|
|
80
|
+
VAULT_FILES_WRITE_BYTES: 'vault.files.writeBytes',
|
|
81
|
+
VAULT_FILES_READ_TEXT: 'vault.files.readText',
|
|
82
|
+
VAULT_FILES_READ_BYTES: 'vault.files.readBytes',
|
|
83
|
+
VAULT_FILES_LIST: 'vault.files.list',
|
|
84
|
+
VAULT_FILES_EXISTS: 'vault.files.exists',
|
|
85
|
+
VAULT_FILES_DELETE: 'vault.files.delete',
|
|
86
|
+
VAULT_FILES_MKDIR: 'vault.files.mkdir',
|
|
87
|
+
VAULT_FILES_MOVE: 'vault.files.move',
|
|
88
|
+
|
|
89
|
+
// Vault - Injection (DEFERRED - returns NOT_IMPLEMENTED)
|
|
90
|
+
VAULT_ATTACH_SECRETS_TO_CONNECTOR: 'vault.attachSecretsToConnector',
|
|
91
|
+
VAULT_INJECT_SECRETS_INTO_RUN: 'vault.injectSecretsIntoRun',
|
|
92
|
+
|
|
93
|
+
// Vault - Audit
|
|
94
|
+
VAULT_AUDIT_LIST: 'vault.audit.list',
|
|
95
|
+
} as const;
|
|
96
|
+
|
|
97
|
+
export type OpName = (typeof OPS)[keyof typeof OPS];
|
|
98
|
+
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// ERROR CODES
|
|
101
|
+
// =============================================================================
|
|
102
|
+
|
|
103
|
+
export const ERROR_CODES = {
|
|
104
|
+
NOT_CONNECTED: 'NOT_CONNECTED',
|
|
105
|
+
NOT_AUTHENTICATED: 'NOT_AUTHENTICATED',
|
|
106
|
+
HOME_GRANT_REQUIRED: 'HOME_GRANT_REQUIRED',
|
|
107
|
+
INVALID_OP: 'INVALID_OP',
|
|
108
|
+
INVALID_PAYLOAD: 'INVALID_PAYLOAD',
|
|
109
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
110
|
+
|
|
111
|
+
// Vault
|
|
112
|
+
VAULT_NOT_INITIALIZED: 'VAULT_NOT_INITIALIZED',
|
|
113
|
+
VAULT_ERROR: 'VAULT_ERROR',
|
|
114
|
+
SECRET_NOT_FOUND: 'SECRET_NOT_FOUND',
|
|
115
|
+
SECRET_ALREADY_EXISTS: 'SECRET_ALREADY_EXISTS',
|
|
116
|
+
BUNDLE_NOT_FOUND: 'BUNDLE_NOT_FOUND',
|
|
117
|
+
BUNDLE_ALREADY_EXISTS: 'BUNDLE_ALREADY_EXISTS',
|
|
118
|
+
|
|
119
|
+
// Files
|
|
120
|
+
FILE_NOT_FOUND: 'FILE_NOT_FOUND',
|
|
121
|
+
FILE_ALREADY_EXISTS: 'FILE_ALREADY_EXISTS',
|
|
122
|
+
DIRECTORY_NOT_EMPTY: 'DIRECTORY_NOT_EMPTY',
|
|
123
|
+
INVALID_PATH: 'INVALID_PATH',
|
|
124
|
+
PATH_TRAVERSAL_DENIED: 'PATH_TRAVERSAL_DENIED',
|
|
125
|
+
|
|
126
|
+
// Deferred
|
|
127
|
+
NOT_IMPLEMENTED: 'NOT_IMPLEMENTED',
|
|
128
|
+
|
|
129
|
+
// Credentials
|
|
130
|
+
INVALID_NODE_ID: 'INVALID_NODE_ID',
|
|
131
|
+
INVALID_NODE_SECRET: 'INVALID_NODE_SECRET',
|
|
132
|
+
CREDENTIALS_STORE_ERROR: 'CREDENTIALS_STORE_ERROR',
|
|
133
|
+
CREDENTIALS_CLEAR_ERROR: 'CREDENTIALS_CLEAR_ERROR',
|
|
134
|
+
CREDENTIALS_NOT_CONFIGURED: 'CREDENTIALS_NOT_CONFIGURED',
|
|
135
|
+
} as const;
|
|
136
|
+
|
|
137
|
+
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
138
|
+
|
|
139
|
+
// =============================================================================
|
|
140
|
+
// CONTRACT VERSION
|
|
141
|
+
// =============================================================================
|
|
142
|
+
|
|
143
|
+
export const CONTRACT_VERSION = 2;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EKKA Error Classes
|
|
3
|
+
*
|
|
4
|
+
* Custom error types for the EKKA client library.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ERROR_CODES } from './constants';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base error class for all EKKA errors.
|
|
11
|
+
*/
|
|
12
|
+
export class EkkaError extends Error {
|
|
13
|
+
readonly code: string;
|
|
14
|
+
|
|
15
|
+
constructor(message: string, code: string) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'EkkaError';
|
|
18
|
+
this.code = code;
|
|
19
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Thrown when an operation is attempted without a connection.
|
|
25
|
+
*/
|
|
26
|
+
export class EkkaNotConnectedError extends EkkaError {
|
|
27
|
+
constructor() {
|
|
28
|
+
super('Not connected. Call ekka.connect() first.', ERROR_CODES.NOT_CONNECTED);
|
|
29
|
+
this.name = 'EkkaNotConnectedError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Thrown when connection fails.
|
|
35
|
+
*/
|
|
36
|
+
export class EkkaConnectionError extends EkkaError {
|
|
37
|
+
constructor(message: string) {
|
|
38
|
+
super(message, 'CONNECTION_ERROR');
|
|
39
|
+
this.name = 'EkkaConnectionError';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Thrown when an API operation fails.
|
|
45
|
+
*/
|
|
46
|
+
export class EkkaApiError extends EkkaError {
|
|
47
|
+
readonly httpStatus?: number;
|
|
48
|
+
|
|
49
|
+
constructor(message: string, code: string, httpStatus?: number) {
|
|
50
|
+
super(message, code);
|
|
51
|
+
this.name = 'EkkaApiError';
|
|
52
|
+
this.httpStatus = httpStatus;
|
|
53
|
+
}
|
|
54
|
+
}
|