eventmodeler 0.4.0 → 0.4.1

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 (61) hide show
  1. package/dist/cloud/slices/index.d.ts +215 -0
  2. package/dist/cloud/slices/index.js +247 -0
  3. package/dist/index.js +984 -39
  4. package/dist/lib/auth.d.ts +24 -0
  5. package/dist/lib/auth.js +332 -0
  6. package/dist/lib/backend.d.ts +48 -0
  7. package/dist/lib/backend.js +103 -0
  8. package/dist/lib/cloud-client.d.ts +70 -0
  9. package/dist/lib/cloud-client.js +528 -0
  10. package/dist/lib/config.d.ts +27 -0
  11. package/dist/lib/config.js +80 -11
  12. package/dist/lib/diff/three-way-merge.js +4 -4
  13. package/dist/lib/file-loader.js +43 -12
  14. package/dist/lib/project-config.d.ts +30 -0
  15. package/dist/lib/project-config.js +90 -0
  16. package/dist/lib/slice-utils.d.ts +28 -0
  17. package/dist/lib/slice-utils.js +80 -0
  18. package/dist/local/slices/index.d.ts +11 -0
  19. package/dist/local/slices/index.js +13 -0
  20. package/dist/projection.js +372 -371
  21. package/dist/slices/add-field/index.js +25 -15
  22. package/dist/slices/add-scenario/index.js +34 -22
  23. package/dist/slices/create-automation-slice/index.js +93 -65
  24. package/dist/slices/create-flow/index.js +24 -18
  25. package/dist/slices/create-state-change-slice/index.js +77 -53
  26. package/dist/slices/create-state-view-slice/index.js +25 -17
  27. package/dist/slices/import/index.d.ts +8 -0
  28. package/dist/slices/import/index.js +63 -0
  29. package/dist/slices/init/index.d.ts +4 -0
  30. package/dist/slices/init/index.js +133 -0
  31. package/dist/slices/list-processors/index.d.ts +3 -0
  32. package/dist/slices/list-processors/index.js +20 -0
  33. package/dist/slices/list-readmodels/index.d.ts +3 -0
  34. package/dist/slices/list-readmodels/index.js +21 -0
  35. package/dist/slices/list-scenarios/index.d.ts +3 -0
  36. package/dist/slices/list-scenarios/index.js +35 -0
  37. package/dist/slices/list-screens/index.d.ts +3 -0
  38. package/dist/slices/list-screens/index.js +47 -0
  39. package/dist/slices/login/index.d.ts +1 -0
  40. package/dist/slices/login/index.js +24 -0
  41. package/dist/slices/logout/index.d.ts +1 -0
  42. package/dist/slices/logout/index.js +14 -0
  43. package/dist/slices/map-fields/index.js +5 -3
  44. package/dist/slices/mark-slice-status/index.js +4 -2
  45. package/dist/slices/remove-field/index.js +25 -15
  46. package/dist/slices/remove-scenario/index.js +8 -4
  47. package/dist/slices/show-aggregate/index.d.ts +3 -0
  48. package/dist/slices/show-aggregate/index.js +108 -0
  49. package/dist/slices/show-processor/index.d.ts +3 -0
  50. package/dist/slices/show-processor/index.js +111 -0
  51. package/dist/slices/show-readmodel/index.d.ts +3 -0
  52. package/dist/slices/show-readmodel/index.js +158 -0
  53. package/dist/slices/show-scenario/index.d.ts +3 -0
  54. package/dist/slices/show-scenario/index.js +196 -0
  55. package/dist/slices/show-screen/index.d.ts +3 -0
  56. package/dist/slices/show-screen/index.js +139 -0
  57. package/dist/slices/update-field/index.js +30 -20
  58. package/dist/slices/whoami/index.d.ts +2 -0
  59. package/dist/slices/whoami/index.js +35 -0
  60. package/dist/types.d.ts +1 -2
  61. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ import { type AuthTokens } from './config.js';
2
+ interface AuthResult {
3
+ success: boolean;
4
+ tokens?: AuthTokens;
5
+ error?: string;
6
+ }
7
+ /**
8
+ * Starts the OAuth flow using Keycloak PKCE.
9
+ * Opens browser and waits for the callback.
10
+ */
11
+ export declare function startAuthFlow(): Promise<AuthResult>;
12
+ /**
13
+ * Refresh the access token using the refresh token.
14
+ */
15
+ export declare function refreshAccessToken(): Promise<AuthResult>;
16
+ /**
17
+ * Get a valid access token, refreshing if needed.
18
+ */
19
+ export declare function getValidAccessToken(): Promise<string | null>;
20
+ /**
21
+ * Log out by clearing stored tokens.
22
+ */
23
+ export declare function logout(): void;
24
+ export {};
@@ -0,0 +1,332 @@
1
+ import * as http from 'node:http';
2
+ import * as crypto from 'node:crypto';
3
+ import { exec } from 'node:child_process';
4
+ import { saveAuthTokens, clearAuthTokens, getAuthTokens, getKeycloakUrl } from './config.js';
5
+ const KEYCLOAK_CLIENT_ID = 'eventmodeler-cli';
6
+ const REDIRECT_URI = 'http://localhost:8787/callback';
7
+ /**
8
+ * Decode the payload of a JWT token (no signature verification - that's the server's job).
9
+ */
10
+ function decodeJwtPayload(token) {
11
+ const parts = token.split('.');
12
+ if (parts.length !== 3)
13
+ throw new Error('Invalid JWT format');
14
+ const payload = Buffer.from(parts[1], 'base64url').toString('utf-8');
15
+ return JSON.parse(payload);
16
+ }
17
+ /**
18
+ * Generate PKCE code verifier and challenge.
19
+ */
20
+ function generatePKCE() {
21
+ // Generate a random code verifier (43-128 characters)
22
+ const codeVerifier = crypto.randomBytes(32).toString('base64url');
23
+ // Generate code challenge using S256 method
24
+ const codeChallenge = crypto
25
+ .createHash('sha256')
26
+ .update(codeVerifier)
27
+ .digest('base64url');
28
+ return { codeVerifier, codeChallenge };
29
+ }
30
+ /**
31
+ * Starts the OAuth flow using Keycloak PKCE.
32
+ * Opens browser and waits for the callback.
33
+ */
34
+ export async function startAuthFlow() {
35
+ return new Promise((resolve) => {
36
+ const keycloakUrl = getKeycloakUrl();
37
+ // Generate PKCE values
38
+ const { codeVerifier, codeChallenge } = generatePKCE();
39
+ // Generate state for CSRF protection
40
+ const state = crypto.randomBytes(16).toString('hex');
41
+ // Create local server to receive callback
42
+ const server = http.createServer(async (req, res) => {
43
+ const url = new URL(req.url ?? '/', `http://localhost:8787`);
44
+ if (url.pathname === '/callback') {
45
+ const code = url.searchParams.get('code');
46
+ const returnedState = url.searchParams.get('state');
47
+ const error = url.searchParams.get('error');
48
+ if (error) {
49
+ res.writeHead(200, { 'Content-Type': 'text/html' });
50
+ res.end(renderHtml('Authentication Failed', `Error: ${error}`, false));
51
+ server.close();
52
+ resolve({ success: false, error });
53
+ return;
54
+ }
55
+ if (returnedState !== state) {
56
+ res.writeHead(200, { 'Content-Type': 'text/html' });
57
+ res.end(renderHtml('Authentication Failed', 'Invalid state parameter. Please try again.', false));
58
+ server.close();
59
+ resolve({ success: false, error: 'Invalid state parameter' });
60
+ return;
61
+ }
62
+ if (!code) {
63
+ res.writeHead(200, { 'Content-Type': 'text/html' });
64
+ res.end(renderHtml('Authentication Failed', 'No authorization code received.', false));
65
+ server.close();
66
+ resolve({ success: false, error: 'No authorization code' });
67
+ return;
68
+ }
69
+ try {
70
+ // Exchange the code for tokens via Keycloak token endpoint (form-urlencoded)
71
+ const tokenUrl = `${keycloakUrl}/protocol/openid-connect/token`;
72
+ const body = new URLSearchParams({
73
+ client_id: KEYCLOAK_CLIENT_ID,
74
+ code,
75
+ code_verifier: codeVerifier,
76
+ grant_type: 'authorization_code',
77
+ redirect_uri: REDIRECT_URI,
78
+ });
79
+ const tokenResponse = await fetch(tokenUrl, {
80
+ method: 'POST',
81
+ headers: {
82
+ 'Content-Type': 'application/x-www-form-urlencoded',
83
+ },
84
+ body: body.toString(),
85
+ });
86
+ if (!tokenResponse.ok) {
87
+ const errorData = await tokenResponse.json().catch(() => ({}));
88
+ throw new Error(errorData.error_description ?? errorData.error ?? `Token exchange failed: ${tokenResponse.status}`);
89
+ }
90
+ const tokenData = await tokenResponse.json();
91
+ // Extract user info from JWT claims
92
+ const claims = decodeJwtPayload(tokenData.access_token);
93
+ const expiresAt = Date.now() + (tokenData.expires_in ?? 3600) * 1000;
94
+ const tokens = {
95
+ accessToken: tokenData.access_token,
96
+ refreshToken: tokenData.refresh_token ?? '',
97
+ expiresAt,
98
+ userId: String(claims.sub ?? ''),
99
+ email: String(claims.email ?? ''),
100
+ };
101
+ saveAuthTokens(tokens);
102
+ res.writeHead(200, { 'Content-Type': 'text/html' });
103
+ res.end(renderHtml('Authentication Successful!', formatSuccessMessage(tokens.email), true));
104
+ server.close();
105
+ resolve({ success: true, tokens });
106
+ }
107
+ catch (err) {
108
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
109
+ res.writeHead(200, { 'Content-Type': 'text/html' });
110
+ res.end(renderHtml('Authentication Failed', errorMessage, false));
111
+ server.close();
112
+ resolve({ success: false, error: errorMessage });
113
+ }
114
+ }
115
+ else {
116
+ res.writeHead(404);
117
+ res.end('Not found');
118
+ }
119
+ });
120
+ server.listen(8787, () => {
121
+ // Build Keycloak authorization URL with PKCE
122
+ const params = new URLSearchParams({
123
+ client_id: KEYCLOAK_CLIENT_ID,
124
+ redirect_uri: REDIRECT_URI,
125
+ response_type: 'code',
126
+ state,
127
+ code_challenge: codeChallenge,
128
+ code_challenge_method: 'S256',
129
+ scope: 'openid email profile',
130
+ });
131
+ const authorizationUrl = `${keycloakUrl}/protocol/openid-connect/auth?${params}`;
132
+ console.log('\nOpening browser for authentication...');
133
+ console.log(`\nIf the browser doesn't open, visit:\n${authorizationUrl}\n`);
134
+ // Open the browser
135
+ openBrowser(authorizationUrl);
136
+ });
137
+ // Timeout after 5 minutes
138
+ setTimeout(() => {
139
+ server.close();
140
+ resolve({ success: false, error: 'Authentication timed out' });
141
+ }, 5 * 60 * 1000);
142
+ });
143
+ }
144
+ /**
145
+ * Refresh the access token using the refresh token.
146
+ */
147
+ export async function refreshAccessToken() {
148
+ const tokens = getAuthTokens();
149
+ if (!tokens?.refreshToken) {
150
+ return { success: false, error: 'No refresh token available' };
151
+ }
152
+ const keycloakUrl = getKeycloakUrl();
153
+ const tokenUrl = `${keycloakUrl}/protocol/openid-connect/token`;
154
+ try {
155
+ const body = new URLSearchParams({
156
+ client_id: KEYCLOAK_CLIENT_ID,
157
+ refresh_token: tokens.refreshToken,
158
+ grant_type: 'refresh_token',
159
+ });
160
+ const response = await fetch(tokenUrl, {
161
+ method: 'POST',
162
+ headers: {
163
+ 'Content-Type': 'application/x-www-form-urlencoded',
164
+ },
165
+ body: body.toString(),
166
+ });
167
+ if (!response.ok) {
168
+ const errorData = await response.json().catch(() => ({}));
169
+ throw new Error(errorData.error_description ?? errorData.error ?? `Token refresh failed: ${response.status}`);
170
+ }
171
+ const tokenData = await response.json();
172
+ // Extract user info from refreshed JWT
173
+ const claims = decodeJwtPayload(tokenData.access_token);
174
+ const newTokens = {
175
+ accessToken: tokenData.access_token,
176
+ refreshToken: tokenData.refresh_token ?? tokens.refreshToken,
177
+ expiresAt: Date.now() + (tokenData.expires_in ?? 3600) * 1000,
178
+ userId: String(claims.sub ?? tokens.userId),
179
+ email: String(claims.email ?? tokens.email),
180
+ };
181
+ saveAuthTokens(newTokens);
182
+ return { success: true, tokens: newTokens };
183
+ }
184
+ catch (err) {
185
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
186
+ return { success: false, error: errorMessage };
187
+ }
188
+ }
189
+ /**
190
+ * Get a valid access token, refreshing if needed.
191
+ */
192
+ export async function getValidAccessToken() {
193
+ const tokens = getAuthTokens();
194
+ if (!tokens)
195
+ return null;
196
+ // If token is expired or will expire in 5 minutes, refresh it
197
+ if (tokens.expiresAt < Date.now() + 5 * 60 * 1000) {
198
+ const result = await refreshAccessToken();
199
+ if (!result.success || !result.tokens)
200
+ return null;
201
+ return result.tokens.accessToken;
202
+ }
203
+ return tokens.accessToken;
204
+ }
205
+ /**
206
+ * Log out by clearing stored tokens.
207
+ */
208
+ export function logout() {
209
+ clearAuthTokens();
210
+ }
211
+ /**
212
+ * Open a URL in the default browser.
213
+ */
214
+ function openBrowser(url) {
215
+ const platform = process.platform;
216
+ let command;
217
+ if (platform === 'darwin') {
218
+ command = `open "${url}"`;
219
+ }
220
+ else if (platform === 'win32') {
221
+ command = `start "" "${url}"`;
222
+ }
223
+ else {
224
+ command = `xdg-open "${url}"`;
225
+ }
226
+ exec(command, (err) => {
227
+ if (err) {
228
+ console.error('Failed to open browser:', err.message);
229
+ }
230
+ });
231
+ }
232
+ /**
233
+ * Render a styled HTML page for the callback matching Event Modeler's design.
234
+ */
235
+ function renderHtml(title, message, success) {
236
+ const iconColor = success ? '#16a34a' : '#dc2626';
237
+ const icon = success
238
+ ? `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>`
239
+ : `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>`;
240
+ return `<!DOCTYPE html>
241
+ <html lang="en">
242
+ <head>
243
+ <meta charset="UTF-8">
244
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
245
+ <title>${title} - Event Modeler</title>
246
+ <style>
247
+ * { box-sizing: border-box; margin: 0; padding: 0; }
248
+ body {
249
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
250
+ display: flex;
251
+ flex-direction: column;
252
+ justify-content: center;
253
+ align-items: center;
254
+ min-height: 100vh;
255
+ background: #fafaf9;
256
+ color: #57534e;
257
+ padding: 1rem;
258
+ }
259
+ .card {
260
+ background: white;
261
+ border: 1px solid #e7e5e4;
262
+ border-radius: 12px;
263
+ padding: 2.5rem 3rem;
264
+ text-align: center;
265
+ max-width: 420px;
266
+ width: 100%;
267
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
268
+ }
269
+ .icon {
270
+ margin-bottom: 1.5rem;
271
+ }
272
+ .logo {
273
+ display: flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ gap: 0.5rem;
277
+ margin-bottom: 2rem;
278
+ font-weight: 600;
279
+ font-size: 1.125rem;
280
+ color: #44403c;
281
+ }
282
+ .logo svg {
283
+ width: 28px;
284
+ height: 28px;
285
+ }
286
+ h1 {
287
+ font-size: 1.5rem;
288
+ font-weight: 600;
289
+ color: #1c1917;
290
+ margin-bottom: 0.5rem;
291
+ }
292
+ .message {
293
+ color: #78716c;
294
+ margin-bottom: 1.5rem;
295
+ line-height: 1.5;
296
+ }
297
+ .hint {
298
+ font-size: 0.875rem;
299
+ color: #a8a29e;
300
+ padding-top: 1.5rem;
301
+ border-top: 1px solid #f5f5f4;
302
+ }
303
+ .email {
304
+ font-weight: 500;
305
+ color: #44403c;
306
+ }
307
+ </style>
308
+ </head>
309
+ <body>
310
+ <div class="card">
311
+ <div class="logo">
312
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
313
+ <path d="M50 50 L50 5 A45 45 0 0 1 89.0 72.5 Z" fill="#D5F1A5"/>
314
+ <path d="M50 50 L89.0 72.5 A45 45 0 0 1 11.0 72.5 Z" fill="#FFB87B"/>
315
+ <path d="M50 50 L11.0 72.5 A45 45 0 0 1 50 5 Z" fill="#B7D3FE"/>
316
+ </svg>
317
+ Event Modeler
318
+ </div>
319
+ <div class="icon">${icon}</div>
320
+ <h1>${title}</h1>
321
+ <p class="message">${message}</p>
322
+ <p class="hint">You can close this window and return to the terminal.</p>
323
+ </div>
324
+ </body>
325
+ </html>`;
326
+ }
327
+ /**
328
+ * Format the success message with email highlighted.
329
+ */
330
+ function formatSuccessMessage(email) {
331
+ return `Signed in as <span class="email">${email}</span>`;
332
+ }
@@ -0,0 +1,48 @@
1
+ import type { EventModel } from '../types.js';
2
+ import { type CloudClient } from './cloud-client.js';
3
+ import { type ProjectConfig } from './project-config.js';
4
+ /**
5
+ * Backend abstraction for CLI operations.
6
+ * Allows commands to work with either local files or cloud backend.
7
+ */
8
+ export interface CliBackend {
9
+ /** Get the current event model */
10
+ getModel(): Promise<EventModel>;
11
+ /** Dispatch a command (for mutations) */
12
+ dispatch(command: unknown): Promise<void>;
13
+ /** Whether this is a cloud backend */
14
+ isCloud(): boolean;
15
+ /** Get the model name or file path */
16
+ getModelIdentifier(): string;
17
+ /** Append a raw event (for local backend only, used by existing commands) */
18
+ appendEvent?(event: unknown): Promise<void>;
19
+ }
20
+ /**
21
+ * Create a local file-based backend.
22
+ */
23
+ export declare function createLocalBackend(filePath: string): CliBackend;
24
+ /**
25
+ * Create a cloud backend using the Axon backend.
26
+ */
27
+ export declare function createCloudBackendFromClient(client: CloudClient, modelId: string, modelName: string): CliBackend;
28
+ export interface ResolveBackendOptions {
29
+ /** Explicit file path (overrides auto-detection) */
30
+ filePath?: string;
31
+ /** Force cloud mode */
32
+ forceCloud?: boolean;
33
+ }
34
+ /**
35
+ * Resolve which backend to use based on:
36
+ * 1. Explicit -f <file> flag
37
+ * 2. .eventmodeler.json project config
38
+ * 3. .eventmodel file in current directory (legacy)
39
+ */
40
+ export declare function resolveBackend(options?: ResolveBackendOptions): Promise<CliBackend>;
41
+ /**
42
+ * Check if we're in a configured project.
43
+ */
44
+ export declare function isInConfiguredProject(): boolean;
45
+ /**
46
+ * Get the project config if available.
47
+ */
48
+ export declare function getProjectConfig(): ProjectConfig | null;
@@ -0,0 +1,103 @@
1
+ import { loadModel, appendEvent as appendEventToFile } from './file-loader.js';
2
+ import { createCloudClient } from './cloud-client.js';
3
+ import { loadProjectConfig } from './project-config.js';
4
+ import { findEventModelFile } from './file-loader.js';
5
+ import { isAuthenticated } from './config.js';
6
+ /**
7
+ * Create a local file-based backend.
8
+ */
9
+ export function createLocalBackend(filePath) {
10
+ return {
11
+ async getModel() {
12
+ return loadModel(filePath);
13
+ },
14
+ async dispatch(_command) {
15
+ // Local backend doesn't use dispatch - existing commands use appendEvent directly
16
+ throw new Error('Local backend does not support dispatch. Use appendEvent instead.');
17
+ },
18
+ isCloud() {
19
+ return false;
20
+ },
21
+ getModelIdentifier() {
22
+ return filePath;
23
+ },
24
+ async appendEvent(event) {
25
+ appendEventToFile(filePath, event);
26
+ },
27
+ };
28
+ }
29
+ /**
30
+ * Create a cloud backend using the Axon backend.
31
+ */
32
+ export function createCloudBackendFromClient(client, modelId, modelName) {
33
+ return {
34
+ async getModel() {
35
+ const model = await client.getModel(modelId);
36
+ if (!model) {
37
+ throw new Error(`Event model not found: ${modelId}`);
38
+ }
39
+ return model;
40
+ },
41
+ async dispatch(command) {
42
+ const result = await client.dispatch(modelId, command);
43
+ if (!result.success) {
44
+ throw new Error('Command dispatch failed');
45
+ }
46
+ },
47
+ isCloud() {
48
+ return true;
49
+ },
50
+ getModelIdentifier() {
51
+ return `${modelName} (${modelId})`;
52
+ },
53
+ };
54
+ }
55
+ /**
56
+ * Resolve which backend to use based on:
57
+ * 1. Explicit -f <file> flag
58
+ * 2. .eventmodeler.json project config
59
+ * 3. .eventmodel file in current directory (legacy)
60
+ */
61
+ export async function resolveBackend(options = {}) {
62
+ // 1. Explicit file path always wins
63
+ if (options.filePath) {
64
+ return createLocalBackend(options.filePath);
65
+ }
66
+ // 2. Check for project config
67
+ const projectConfig = loadProjectConfig();
68
+ if (projectConfig) {
69
+ if (projectConfig.type === 'local') {
70
+ return createLocalBackend(projectConfig.file);
71
+ }
72
+ if (projectConfig.type === 'cloud') {
73
+ if (!isAuthenticated()) {
74
+ throw new Error('Not authenticated. Run "eventmodeler login" first.');
75
+ }
76
+ const client = await createCloudClient();
77
+ return createCloudBackendFromClient(client, projectConfig.modelId, projectConfig.modelName);
78
+ }
79
+ }
80
+ // 3. Legacy: look for .eventmodel file in current directory
81
+ const localFile = await findEventModelFile();
82
+ if (localFile) {
83
+ return createLocalBackend(localFile);
84
+ }
85
+ // 4. No backend found
86
+ throw new Error('No event model found.\n\n' +
87
+ 'Options:\n' +
88
+ ' - Run "eventmodeler init" to set up a project\n' +
89
+ ' - Use "-f <file>" to specify a local .eventmodel file\n' +
90
+ ' - Create a .eventmodel file in the current directory');
91
+ }
92
+ /**
93
+ * Check if we're in a configured project.
94
+ */
95
+ export function isInConfiguredProject() {
96
+ return loadProjectConfig() !== null;
97
+ }
98
+ /**
99
+ * Get the project config if available.
100
+ */
101
+ export function getProjectConfig() {
102
+ return loadProjectConfig();
103
+ }
@@ -0,0 +1,70 @@
1
+ import type { EventModel, RawEvent } from '../types.js';
2
+ export interface ImportResult {
3
+ success: boolean;
4
+ modelId: string;
5
+ eventCount: number;
6
+ }
7
+ export interface CloudClient {
8
+ listModels(): Promise<CloudModelInfo[]>;
9
+ getModel(modelId: string): Promise<EventModel | null>;
10
+ dispatch(modelId: string, command: unknown): Promise<{
11
+ success: boolean;
12
+ events: unknown[];
13
+ }>;
14
+ importModel(modelId: string, modelName: string, events: RawEvent[]): Promise<ImportResult>;
15
+ addField(modelId: string, elementType: string, elementName: string, field: Record<string, unknown>): Promise<{
16
+ success: boolean;
17
+ }>;
18
+ removeField(modelId: string, elementType: string, elementName: string, fieldName: string): Promise<{
19
+ success: boolean;
20
+ }>;
21
+ updateField(modelId: string, elementType: string, elementName: string, fieldName: string, updates: Record<string, unknown>): Promise<{
22
+ success: boolean;
23
+ }>;
24
+ createStateChangeSlice(modelId: string, sliceName: string, after: string, before: string, screen: any, command: any, event: any): Promise<{
25
+ sliceId: string;
26
+ screenId: string;
27
+ commandId: string;
28
+ eventId: string;
29
+ }>;
30
+ createStateViewSlice(modelId: string, sliceName: string, after: string, before: string, readModel: any): Promise<{
31
+ sliceId: string;
32
+ readModelId: string;
33
+ }>;
34
+ createAutomationSlice(modelId: string, sliceName: string, after: string, before: string, readModel: any, processor: any, command: any, event: any): Promise<{
35
+ sliceId: string;
36
+ readModelId: string;
37
+ processorId: string;
38
+ commandId: string;
39
+ eventId: string;
40
+ }>;
41
+ createFlow(modelId: string, fromName: string, toName: string): Promise<{
42
+ flowId: string;
43
+ }>;
44
+ markSliceStatus(modelId: string, sliceName: string, status: string): Promise<{
45
+ success: boolean;
46
+ }>;
47
+ createScenario(modelId: string, sliceName: string, scenario: {
48
+ name: string;
49
+ }): Promise<{
50
+ scenarioId: string;
51
+ }>;
52
+ removeScenario(modelId: string, scenarioName: string, sliceName?: string): Promise<{
53
+ success: boolean;
54
+ }>;
55
+ mapFields(modelId: string, sourceName: string, targetName: string, mappings: Array<{
56
+ from: string;
57
+ to: string;
58
+ }>): Promise<{
59
+ success: boolean;
60
+ }>;
61
+ }
62
+ export interface CloudModelInfo {
63
+ modelId: string;
64
+ name: string;
65
+ updatedAt?: string;
66
+ }
67
+ /**
68
+ * Create a cloud client for interacting with the Axon backend.
69
+ */
70
+ export declare function createCloudClient(): Promise<CloudClient>;