@camunda8/cli 2.0.0-alpha.2 → 2.0.0-alpha.3

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 (41) hide show
  1. package/README.md +97 -22
  2. package/dist/commands/completion.d.ts +8 -0
  3. package/dist/commands/completion.d.ts.map +1 -0
  4. package/dist/commands/completion.js +596 -0
  5. package/dist/commands/completion.js.map +1 -0
  6. package/dist/commands/deployments.d.ts.map +1 -1
  7. package/dist/commands/deployments.js +2 -1
  8. package/dist/commands/deployments.js.map +1 -1
  9. package/dist/commands/help.d.ts.map +1 -1
  10. package/dist/commands/help.js +23 -5
  11. package/dist/commands/help.js.map +1 -1
  12. package/dist/commands/process-definitions.d.ts +17 -0
  13. package/dist/commands/process-definitions.d.ts.map +1 -0
  14. package/dist/commands/process-definitions.js +61 -0
  15. package/dist/commands/process-definitions.js.map +1 -0
  16. package/dist/commands/profiles.d.ts +17 -8
  17. package/dist/commands/profiles.d.ts.map +1 -1
  18. package/dist/commands/profiles.js +74 -35
  19. package/dist/commands/profiles.js.map +1 -1
  20. package/dist/commands/session.d.ts.map +1 -1
  21. package/dist/commands/session.js +7 -7
  22. package/dist/commands/session.js.map +1 -1
  23. package/dist/config.d.ts +109 -52
  24. package/dist/config.d.ts.map +1 -1
  25. package/dist/config.js +350 -179
  26. package/dist/config.js.map +1 -1
  27. package/dist/index.js +35 -6
  28. package/dist/index.js.map +1 -1
  29. package/dist/logger.d.ts +1 -2
  30. package/dist/logger.d.ts.map +1 -1
  31. package/dist/logger.js +20 -14
  32. package/dist/logger.js.map +1 -1
  33. package/dist/plugin-loader.d.ts +9 -0
  34. package/dist/plugin-loader.d.ts.map +1 -1
  35. package/dist/plugin-loader.js +82 -23
  36. package/dist/plugin-loader.js.map +1 -1
  37. package/dist/runtime.d.ts +18 -5
  38. package/dist/runtime.d.ts.map +1 -1
  39. package/dist/runtime.js +31 -6
  40. package/dist/runtime.js.map +1 -1
  41. package/package.json +12 -12
package/dist/config.js CHANGED
@@ -1,15 +1,108 @@
1
1
  /**
2
2
  * Configuration and session state management for c8ctl
3
- * Handles profiles, session state, and credential resolution
3
+ *
4
+ * c8ctl stores its own profiles in DATA_DIR/c8ctl/profiles.json
5
+ * Modeler connections are read from settings.json (read-only) with "modeler:" prefix
4
6
  */
5
7
  import { homedir, platform } from 'node:os';
6
8
  import { join } from 'node:path';
7
9
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
10
+ import { c8ctl } from "./runtime.js";
11
+ // ============================================================================
12
+ // Constants - matching Camunda Modeler exactly
13
+ // ============================================================================
14
+ export const TARGET_TYPES = {
15
+ CAMUNDA_CLOUD: 'camundaCloud',
16
+ SELF_HOSTED: 'selfHosted',
17
+ };
18
+ export const AUTH_TYPES = {
19
+ NONE: 'none',
20
+ BASIC: 'basic',
21
+ OAUTH: 'oauth',
22
+ };
23
+ // ============================================================================
24
+ // Validation - matching Camunda Modeler's validation rules
25
+ // ============================================================================
26
+ const VALIDATION_PATTERNS = {
27
+ URL: /^(http|grpc)s?:\/\//,
28
+ CAMUNDA_CLOUD_GRPC_URL: /^((https|grpcs):\/\/|)[a-z\d-]+\.[a-z]+-\d+\.zeebe\.camunda\.io(:443|)\/?$/,
29
+ CAMUNDA_CLOUD_REST_URL: /^https:\/\/[a-z]+-\d+\.zeebe\.camunda\.io(:443|)\/[a-z\d-]+\/?$/,
30
+ };
31
+ // Combined pattern for cloud URLs
32
+ const CAMUNDA_CLOUD_URL_PATTERN = new RegExp(`${VALIDATION_PATTERNS.CAMUNDA_CLOUD_GRPC_URL.source}|${VALIDATION_PATTERNS.CAMUNDA_CLOUD_REST_URL.source}`);
33
+ /**
34
+ * Validate a connection configuration
35
+ * Returns array of error messages (empty if valid)
36
+ */
37
+ export function validateConnection(conn) {
38
+ const errors = [];
39
+ if (!conn) {
40
+ errors.push('Connection configuration is required');
41
+ return errors;
42
+ }
43
+ if (!conn.id) {
44
+ errors.push('Connection must have an ID');
45
+ }
46
+ if (!conn.targetType) {
47
+ errors.push('Target type is required (camundaCloud or selfHosted)');
48
+ return errors;
49
+ }
50
+ if (conn.targetType === TARGET_TYPES.CAMUNDA_CLOUD) {
51
+ if (!conn.camundaCloudClusterUrl) {
52
+ errors.push('Cluster URL is required for Camunda Cloud');
53
+ }
54
+ else if (!CAMUNDA_CLOUD_URL_PATTERN.test(conn.camundaCloudClusterUrl)) {
55
+ errors.push('Cluster URL must be a valid Camunda 8 SaaS URL');
56
+ }
57
+ if (!conn.camundaCloudClientId) {
58
+ errors.push('Client ID is required for Camunda Cloud');
59
+ }
60
+ if (!conn.camundaCloudClientSecret) {
61
+ errors.push('Client Secret is required for Camunda Cloud');
62
+ }
63
+ }
64
+ else if (conn.targetType === TARGET_TYPES.SELF_HOSTED) {
65
+ if (!conn.contactPoint) {
66
+ errors.push('Cluster URL (contactPoint) is required for Self-Hosted');
67
+ }
68
+ else if (!VALIDATION_PATTERNS.URL.test(conn.contactPoint)) {
69
+ errors.push('Cluster URL must start with http://, https://, grpc://, or grpcs://');
70
+ }
71
+ if (conn.authType === AUTH_TYPES.BASIC) {
72
+ if (!conn.basicAuthUsername) {
73
+ errors.push('Username is required for Basic authentication');
74
+ }
75
+ if (!conn.basicAuthPassword) {
76
+ errors.push('Password is required for Basic authentication');
77
+ }
78
+ }
79
+ else if (conn.authType === AUTH_TYPES.OAUTH) {
80
+ if (!conn.clientId) {
81
+ errors.push('Client ID is required for OAuth authentication');
82
+ }
83
+ if (!conn.clientSecret) {
84
+ errors.push('Client Secret is required for OAuth authentication');
85
+ }
86
+ if (!conn.oauthURL) {
87
+ errors.push('OAuth URL is required for OAuth authentication');
88
+ }
89
+ if (!conn.audience) {
90
+ errors.push('Audience is required for OAuth authentication');
91
+ }
92
+ }
93
+ }
94
+ else {
95
+ errors.push(`Unknown target type: ${conn.targetType}`);
96
+ }
97
+ return errors;
98
+ }
99
+ // ============================================================================
100
+ // Directory and Path Utilities
101
+ // ============================================================================
8
102
  /**
9
- * Get platform-specific user data directory
103
+ * Get platform-specific user data directory for c8ctl
10
104
  */
11
105
  export function getUserDataDir() {
12
- // Allow override for testing
13
106
  if (process.env.C8CTL_DATA_DIR) {
14
107
  return process.env.C8CTL_DATA_DIR;
15
108
  }
@@ -20,14 +113,19 @@ export function getUserDataDir() {
20
113
  return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'c8ctl');
21
114
  case 'darwin':
22
115
  return join(home, 'Library', 'Application Support', 'c8ctl');
23
- default: // linux and others
24
- return join(process.env.XDG_DATA_HOME || join(home, '.local', 'share'), 'c8ctl');
116
+ default:
117
+ return join(process.env.XDG_CONFIG_HOME || join(home, '.config'), 'c8ctl');
25
118
  }
26
119
  }
27
120
  /**
28
121
  * Get platform-specific Camunda Modeler data directory
122
+ * Modeler stores connections in settings.json
29
123
  */
30
124
  export function getModelerDataDir() {
125
+ // Allow override for testing
126
+ if (process.env.C8CTL_MODELER_DIR) {
127
+ return process.env.C8CTL_MODELER_DIR;
128
+ }
31
129
  const plat = platform();
32
130
  const home = homedir();
33
131
  switch (plat) {
@@ -35,13 +133,10 @@ export function getModelerDataDir() {
35
133
  return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'camunda-modeler');
36
134
  case 'darwin':
37
135
  return join(home, 'Library', 'Application Support', 'camunda-modeler');
38
- default: // linux and others
136
+ default:
39
137
  return join(process.env.XDG_CONFIG_HOME || join(home, '.config'), 'camunda-modeler');
40
138
  }
41
139
  }
42
- /**
43
- * Ensure user data directory exists
44
- */
45
140
  function ensureUserDataDir() {
46
141
  const dir = getUserDataDir();
47
142
  if (!existsSync(dir)) {
@@ -49,63 +144,53 @@ function ensureUserDataDir() {
49
144
  }
50
145
  return dir;
51
146
  }
52
- /**
53
- * Get profiles file path
54
- */
147
+ function getSessionStatePath() {
148
+ return join(ensureUserDataDir(), 'session.json');
149
+ }
55
150
  function getProfilesPath() {
56
151
  return join(ensureUserDataDir(), 'profiles.json');
57
152
  }
58
- /**
59
- * Get session state file path
60
- */
61
- function getSessionStatePath() {
62
- return join(ensureUserDataDir(), 'session.json');
153
+ function getModelerSettingsPath() {
154
+ return join(getModelerDataDir(), 'settings.json');
63
155
  }
64
156
  /**
65
- * Load all profiles
157
+ * Load c8ctl profiles from profiles.json
66
158
  */
67
159
  export function loadProfiles() {
68
- const path = getProfilesPath();
69
- if (!existsSync(path)) {
160
+ const profilesPath = getProfilesPath();
161
+ if (!existsSync(profilesPath)) {
70
162
  return [];
71
163
  }
72
164
  try {
73
- const data = readFileSync(path, 'utf-8');
74
- return JSON.parse(data);
165
+ const data = readFileSync(profilesPath, 'utf-8');
166
+ const profilesFile = JSON.parse(data);
167
+ return profilesFile.profiles || [];
75
168
  }
76
- catch (error) {
169
+ catch {
77
170
  return [];
78
171
  }
79
172
  }
80
173
  /**
81
- * Save profiles to disk
174
+ * Save c8ctl profiles to profiles.json
82
175
  */
83
176
  export function saveProfiles(profiles) {
84
- const path = getProfilesPath();
85
- writeFileSync(path, JSON.stringify(profiles, null, 2), 'utf-8');
177
+ const profilesPath = getProfilesPath();
178
+ const profilesFile = { profiles };
179
+ writeFileSync(profilesPath, JSON.stringify(profilesFile, null, 2), 'utf-8');
86
180
  }
87
181
  /**
88
- * Get a profile by name
89
- * Supports both c8ctl profiles and modeler profiles (with 'modeler:' prefix)
182
+ * Get a c8ctl profile by name
90
183
  */
91
184
  export function getProfile(name) {
92
- // Check if this is a modeler profile request
93
- if (name.startsWith('modeler:')) {
94
- const modelerProfile = getModelerProfile(name);
95
- if (modelerProfile) {
96
- return convertModelerProfile(modelerProfile);
97
- }
98
- return undefined;
99
- }
100
- // Check c8ctl profiles
101
185
  const profiles = loadProfiles();
102
186
  return profiles.find(p => p.name === name);
103
187
  }
104
188
  /**
105
- * Add or update a profile
189
+ * Add a c8ctl profile
106
190
  */
107
191
  export function addProfile(profile) {
108
192
  const profiles = loadProfiles();
193
+ // Check if profile already exists
109
194
  const existingIndex = profiles.findIndex(p => p.name === profile.name);
110
195
  if (existingIndex >= 0) {
111
196
  profiles[existingIndex] = profile;
@@ -116,98 +201,276 @@ export function addProfile(profile) {
116
201
  saveProfiles(profiles);
117
202
  }
118
203
  /**
119
- * Remove a profile
204
+ * Remove a c8ctl profile by name
120
205
  */
121
206
  export function removeProfile(name) {
122
207
  const profiles = loadProfiles();
123
208
  const filtered = profiles.filter(p => p.name !== name);
124
209
  if (filtered.length === profiles.length) {
125
- return false; // Profile not found
210
+ return false;
126
211
  }
127
212
  saveProfiles(filtered);
128
213
  return true;
129
214
  }
215
+ // ============================================================================
216
+ // Modeler Connection Management - READ-ONLY from settings.json
217
+ // ============================================================================
218
+ export const MODELER_PREFIX = 'modeler:';
130
219
  /**
131
- * Load session state
220
+ * Load connections from Modeler's settings.json (read-only)
221
+ * These are NOT modified by c8ctl
222
+ */
223
+ export function loadModelerConnections() {
224
+ const settingsPath = getModelerSettingsPath();
225
+ if (!existsSync(settingsPath)) {
226
+ return [];
227
+ }
228
+ try {
229
+ const data = readFileSync(settingsPath, 'utf-8');
230
+ const settings = JSON.parse(data);
231
+ const connections = settings['connectionManagerPlugin.c8connections'];
232
+ if (!connections || !Array.isArray(connections)) {
233
+ return [];
234
+ }
235
+ // Filter out invalid connections (must have id)
236
+ return connections.filter(c => c && c.id);
237
+ }
238
+ catch {
239
+ return [];
240
+ }
241
+ }
242
+ /**
243
+ * Get all profiles including c8ctl profiles and Modeler connections
244
+ * Modeler connections are prefixed with "modeler:"
245
+ */
246
+ export function getAllProfiles() {
247
+ const c8ctlProfiles = loadProfiles();
248
+ const modelerConnections = loadModelerConnections();
249
+ // Convert Modeler connections to Profile format with "modeler:" prefix
250
+ const modelerProfiles = modelerConnections.map(connectionToProfile).map(p => ({
251
+ ...p,
252
+ name: `${MODELER_PREFIX}${p.name}`,
253
+ }));
254
+ return [...c8ctlProfiles, ...modelerProfiles];
255
+ }
256
+ /**
257
+ * Get a profile by name, checking both c8ctl and Modeler sources
258
+ * For Modeler profiles, accepts name with or without "modeler:" prefix
259
+ */
260
+ export function getProfileOrModeler(name) {
261
+ // Try c8ctl profiles first
262
+ const c8ctlProfile = getProfile(name);
263
+ if (c8ctlProfile) {
264
+ return c8ctlProfile;
265
+ }
266
+ // Try Modeler connections (with or without prefix)
267
+ const modelerName = name.startsWith(MODELER_PREFIX) ? name.slice(MODELER_PREFIX.length) : name;
268
+ const modelerConnections = loadModelerConnections();
269
+ const modelerConnection = modelerConnections.find(c => c.name === modelerName || c.id === modelerName);
270
+ if (modelerConnection) {
271
+ const profile = connectionToProfile(modelerConnection);
272
+ return {
273
+ ...profile,
274
+ name: `${MODELER_PREFIX}${profile.name}`,
275
+ };
276
+ }
277
+ return undefined;
278
+ }
279
+ // ============================================================================
280
+ // Conversion Utilities
281
+ // ============================================================================
282
+ /**
283
+ * Convert a Connection to ClusterConfig for API client use
284
+ */
285
+ export function connectionToClusterConfig(conn) {
286
+ if (conn.targetType === TARGET_TYPES.CAMUNDA_CLOUD) {
287
+ return {
288
+ baseUrl: conn.camundaCloudClusterUrl || '',
289
+ clientId: conn.camundaCloudClientId,
290
+ clientSecret: conn.camundaCloudClientSecret,
291
+ audience: conn.camundaCloudClusterUrl, // Cloud uses URL as audience
292
+ oAuthUrl: 'https://login.cloud.camunda.io/oauth/token',
293
+ };
294
+ }
295
+ // Self-hosted
296
+ const config = {
297
+ baseUrl: conn.contactPoint || 'http://localhost:8080/v2',
298
+ };
299
+ if (conn.authType === AUTH_TYPES.BASIC) {
300
+ config.username = conn.basicAuthUsername;
301
+ config.password = conn.basicAuthPassword;
302
+ }
303
+ else if (conn.authType === AUTH_TYPES.OAUTH) {
304
+ config.clientId = conn.clientId;
305
+ config.clientSecret = conn.clientSecret;
306
+ config.oAuthUrl = conn.oauthURL;
307
+ config.audience = conn.audience;
308
+ }
309
+ return config;
310
+ }
311
+ /**
312
+ * Convert Connection to Profile format
313
+ * Used to convert read-only Modeler connections to c8ctl Profile format
314
+ */
315
+ export function connectionToProfile(conn) {
316
+ const config = connectionToClusterConfig(conn);
317
+ return {
318
+ name: conn.name || conn.id,
319
+ baseUrl: config.baseUrl,
320
+ clientId: config.clientId,
321
+ clientSecret: config.clientSecret,
322
+ audience: config.audience,
323
+ oAuthUrl: config.oAuthUrl,
324
+ username: config.username,
325
+ password: config.password,
326
+ defaultTenantId: conn.tenantId,
327
+ };
328
+ }
329
+ /**
330
+ * Convert Profile to ClusterConfig for API client use
331
+ */
332
+ export function profileToClusterConfig(profile) {
333
+ return {
334
+ baseUrl: profile.baseUrl,
335
+ clientId: profile.clientId,
336
+ clientSecret: profile.clientSecret,
337
+ audience: profile.audience,
338
+ oAuthUrl: profile.oAuthUrl,
339
+ username: profile.username,
340
+ password: profile.password,
341
+ };
342
+ }
343
+ /**
344
+ * Get display label for a connection
345
+ */
346
+ export function getConnectionLabel(conn) {
347
+ if (conn.name) {
348
+ return conn.name;
349
+ }
350
+ // Fallback to URL-based label like Modeler does
351
+ const url = conn.targetType === TARGET_TYPES.CAMUNDA_CLOUD
352
+ ? conn.camundaCloudClusterUrl
353
+ : conn.contactPoint;
354
+ return url ? `Unnamed (${url})` : 'Unnamed connection';
355
+ }
356
+ /**
357
+ * Get auth type label for display
358
+ */
359
+ export function getAuthTypeLabel(conn) {
360
+ if (conn.targetType === TARGET_TYPES.CAMUNDA_CLOUD) {
361
+ return 'OAuth (Cloud)';
362
+ }
363
+ switch (conn.authType) {
364
+ case AUTH_TYPES.BASIC:
365
+ return 'Basic';
366
+ case AUTH_TYPES.OAUTH:
367
+ return 'OAuth';
368
+ case AUTH_TYPES.NONE:
369
+ default:
370
+ return 'None';
371
+ }
372
+ }
373
+ /**
374
+ * Get target type label for display
375
+ */
376
+ export function getTargetTypeLabel(conn) {
377
+ return conn.targetType === TARGET_TYPES.CAMUNDA_CLOUD
378
+ ? 'Camunda Cloud'
379
+ : 'Self-Hosted';
380
+ }
381
+ // ============================================================================
382
+ // Session State Management
383
+ // ============================================================================
384
+ /**
385
+ * Load session state from disk and populate c8ctl runtime object
132
386
  */
133
387
  export function loadSessionState() {
134
388
  const path = getSessionStatePath();
135
389
  if (!existsSync(path)) {
136
- return { outputMode: 'text' };
390
+ return {
391
+ activeProfile: c8ctl.activeProfile,
392
+ activeTenant: c8ctl.activeTenant,
393
+ outputMode: c8ctl.outputMode,
394
+ };
137
395
  }
138
396
  try {
139
397
  const data = readFileSync(path, 'utf-8');
140
- return JSON.parse(data);
398
+ const state = JSON.parse(data);
399
+ c8ctl.activeProfile = state.activeProfile === null ? undefined : state.activeProfile;
400
+ c8ctl.activeTenant = state.activeTenant === null ? undefined : state.activeTenant;
401
+ c8ctl.outputMode = state.outputMode || 'text';
402
+ return {
403
+ activeProfile: c8ctl.activeProfile,
404
+ activeTenant: c8ctl.activeTenant,
405
+ outputMode: c8ctl.outputMode,
406
+ };
141
407
  }
142
- catch (error) {
143
- return { outputMode: 'text' };
408
+ catch {
409
+ return {
410
+ activeProfile: c8ctl.activeProfile,
411
+ activeTenant: c8ctl.activeTenant,
412
+ outputMode: c8ctl.outputMode,
413
+ };
144
414
  }
145
415
  }
146
416
  /**
147
- * Save session state to disk
417
+ * Save session state from c8ctl runtime object to disk
148
418
  */
149
419
  export function saveSessionState(state) {
420
+ const stateToSave = {
421
+ activeProfile: state?.activeProfile ?? c8ctl.activeProfile,
422
+ activeTenant: state?.activeTenant ?? c8ctl.activeTenant,
423
+ outputMode: state?.outputMode ?? c8ctl.outputMode,
424
+ };
425
+ if (state) {
426
+ c8ctl.activeProfile = state.activeProfile;
427
+ c8ctl.activeTenant = state.activeTenant;
428
+ c8ctl.outputMode = state.outputMode;
429
+ }
150
430
  const path = getSessionStatePath();
151
- writeFileSync(path, JSON.stringify(state, null, 2), 'utf-8');
431
+ writeFileSync(path, JSON.stringify(stateToSave, (_, value) => (value === undefined ? null : value), 2), 'utf-8');
152
432
  }
153
433
  /**
154
- * Set active profile in session
434
+ * Set active profile/connection in session and persist to disk
155
435
  */
156
436
  export function setActiveProfile(name) {
157
- const state = loadSessionState();
158
- state.activeProfile = name;
159
- saveSessionState(state);
437
+ c8ctl.activeProfile = name;
438
+ saveSessionState();
160
439
  }
161
440
  /**
162
- * Set active tenant in session
441
+ * Set active tenant in session and persist to disk
163
442
  */
164
443
  export function setActiveTenant(tenantId) {
165
- const state = loadSessionState();
166
- state.activeTenant = tenantId;
167
- saveSessionState(state);
444
+ c8ctl.activeTenant = tenantId;
445
+ saveSessionState();
168
446
  }
169
447
  /**
170
- * Set output mode in session
448
+ * Set output mode in session and persist to disk
171
449
  */
172
450
  export function setOutputMode(mode) {
173
- const state = loadSessionState();
174
- state.outputMode = mode;
175
- saveSessionState(state);
451
+ c8ctl.outputMode = mode;
452
+ saveSessionState();
176
453
  }
454
+ // ============================================================================
455
+ // Cluster Configuration Resolution
456
+ // ============================================================================
177
457
  /**
178
458
  * Resolve cluster configuration from session, flags, env vars, or defaults
179
459
  * Priority: profileFlag → session profile → env vars → localhost fallback
180
460
  */
181
461
  export function resolveClusterConfig(profileFlag) {
182
- // 1. Try profile flag
462
+ // 1. Try profile flag (profile name, including modeler: prefix)
183
463
  if (profileFlag) {
184
- const profile = getProfile(profileFlag);
464
+ const profile = getProfileOrModeler(profileFlag);
185
465
  if (profile) {
186
- return {
187
- baseUrl: profile.baseUrl,
188
- clientId: profile.clientId,
189
- clientSecret: profile.clientSecret,
190
- audience: profile.audience,
191
- oAuthUrl: profile.oAuthUrl,
192
- username: profile.username,
193
- password: profile.password,
194
- };
466
+ return profileToClusterConfig(profile);
195
467
  }
196
468
  }
197
469
  // 2. Try session profile
198
- const session = loadSessionState();
199
- if (session.activeProfile) {
200
- const profile = getProfile(session.activeProfile);
470
+ if (c8ctl.activeProfile) {
471
+ const profile = getProfileOrModeler(c8ctl.activeProfile);
201
472
  if (profile) {
202
- return {
203
- baseUrl: profile.baseUrl,
204
- clientId: profile.clientId,
205
- clientSecret: profile.clientSecret,
206
- audience: profile.audience,
207
- oAuthUrl: profile.oAuthUrl,
208
- username: profile.username,
209
- password: profile.password,
210
- };
473
+ return profileToClusterConfig(profile);
211
474
  }
212
475
  }
213
476
  // 3. Try environment variables
@@ -230,8 +493,6 @@ export function resolveClusterConfig(profileFlag) {
230
493
  };
231
494
  }
232
495
  // 4. Localhost fallback with basic auth (demo/demo)
233
- // These default credentials match the docker-compose configuration
234
- // and are intended for local development only
235
496
  return {
236
497
  baseUrl: 'http://localhost:8080/v2',
237
498
  username: 'demo',
@@ -240,18 +501,17 @@ export function resolveClusterConfig(profileFlag) {
240
501
  }
241
502
  /**
242
503
  * Resolve tenant ID from session, profile, env vars, or default
243
- * Priority: session tenant → profile default tenant → env var → '<default>'
504
+ * Priority: session tenant → profile tenant → env var → '<default>'
244
505
  */
245
506
  export function resolveTenantId(profileFlag) {
246
507
  // 1. Try session tenant
247
- const session = loadSessionState();
248
- if (session.activeTenant) {
249
- return session.activeTenant;
508
+ if (c8ctl.activeTenant) {
509
+ return c8ctl.activeTenant;
250
510
  }
251
511
  // 2. Try profile default tenant (from flag or session)
252
- const profileName = profileFlag || session.activeProfile;
512
+ const profileName = profileFlag || c8ctl.activeProfile;
253
513
  if (profileName) {
254
- const profile = getProfile(profileName);
514
+ const profile = getProfileOrModeler(profileName);
255
515
  if (profile?.defaultTenantId) {
256
516
  return profile.defaultTenantId;
257
517
  }
@@ -264,93 +524,4 @@ export function resolveTenantId(profileFlag) {
264
524
  // 4. Default tenant
265
525
  return '<default>';
266
526
  }
267
- /**
268
- * Load Camunda Modeler profiles from profiles.json
269
- * Always reads fresh from disk (no caching)
270
- *
271
- * TODO: Consider introducing caching mechanism for better performance.
272
- * Current implementation reads from disk on every call. For commands that
273
- * list profiles or look up multiple profiles, this could be optimized by
274
- * implementing per-execution memoization or a time-based cache.
275
- */
276
- export function loadModelerProfiles() {
277
- try {
278
- const modelerDir = getModelerDataDir();
279
- const profilesPath = join(modelerDir, 'profiles.json');
280
- if (!existsSync(profilesPath)) {
281
- return [];
282
- }
283
- const data = readFileSync(profilesPath, 'utf-8');
284
- const parsed = JSON.parse(data);
285
- return parsed.profiles || [];
286
- }
287
- catch (error) {
288
- // Silently return empty array if file can't be read or parsed
289
- return [];
290
- }
291
- }
292
- /**
293
- * Get a modeler profile by name or cluster ID
294
- * Accepts 'modeler:name' or 'modeler:id' format, or just 'name'/'id'
295
- */
296
- export function getModelerProfile(identifier) {
297
- const profiles = loadModelerProfiles();
298
- // Remove 'modeler:' prefix if present
299
- const searchId = identifier.startsWith('modeler:')
300
- ? identifier.substring(8)
301
- : identifier;
302
- // Search by name first, then by clusterId
303
- return profiles.find(p => p.name === searchId || p.clusterId === searchId);
304
- }
305
- /**
306
- * Construct REST API URL from modeler profile
307
- * For cloud: uses clusterUrl as-is (Camunda cloud URLs don't need /v2)
308
- * For self-managed: localhost URLs get /v2 appended
309
- * Does not derive values - uses what's provided
310
- *
311
- * Note: Self-managed clusters should include /v2 in their clusterUrl if needed
312
- */
313
- export function constructApiUrl(profile) {
314
- // If clusterUrl is provided, use it as the base
315
- if (profile.clusterUrl) {
316
- const url = profile.clusterUrl;
317
- // If it already has /v2 endpoint, use as-is
318
- if (url.includes('/v2')) {
319
- return url;
320
- }
321
- // Only append /v2 for localhost URLs
322
- // Self-managed clusters should include /v2 in their clusterUrl if needed
323
- if (url.includes('localhost') || url.includes('127.0.0.1')) {
324
- return `${url.replace(/\/$/, '')}/v2`;
325
- }
326
- // For all other URLs (including cloud), use as-is
327
- return url;
328
- }
329
- // If no clusterUrl but have clusterId, construct cloud URL
330
- if (profile.clusterId) {
331
- // Cloud cluster URLs follow pattern: https://{clusterId}.{region}.zeebe.camunda.io
332
- // We can't derive the region, so just use the clusterId as a fallback base
333
- return `https://${profile.clusterId}.zeebe.camunda.io`;
334
- }
335
- // Fallback to localhost
336
- return 'http://localhost:8080/v2';
337
- }
338
- /**
339
- * Convert a modeler profile to a c8ctl Profile
340
- */
341
- export function convertModelerProfile(modelerProfile) {
342
- const name = modelerProfile.name || modelerProfile.clusterId || 'unknown';
343
- const baseUrl = constructApiUrl(modelerProfile);
344
- return {
345
- name: `modeler:${name}`,
346
- baseUrl,
347
- clientId: modelerProfile.clientId,
348
- clientSecret: modelerProfile.clientSecret,
349
- audience: modelerProfile.audience,
350
- // Cloud clusters typically use the standard OAuth URL
351
- oAuthUrl: modelerProfile.audience ?
352
- 'https://login.cloud.camunda.io/oauth/token' :
353
- undefined,
354
- };
355
- }
356
527
  //# sourceMappingURL=config.js.map