hereya-cli 0.89.0 → 0.90.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 (51) hide show
  1. package/README.md +65 -65
  2. package/dist/backend/cloud/cloud-backend/apps-deploy.d.ts +69 -0
  3. package/dist/backend/cloud/cloud-backend/apps-deploy.js +94 -0
  4. package/dist/backend/cloud/cloud-backend/apps-versions.d.ts +69 -0
  5. package/dist/backend/cloud/cloud-backend/apps-versions.js +113 -0
  6. package/dist/backend/cloud/cloud-backend/executor-jobs.d.ts +75 -0
  7. package/dist/backend/cloud/cloud-backend/executor-jobs.js +110 -0
  8. package/dist/backend/cloud/cloud-backend/misc.d.ts +5 -0
  9. package/dist/backend/cloud/cloud-backend/misc.js +78 -0
  10. package/dist/backend/cloud/cloud-backend/packages-publish.d.ts +3 -0
  11. package/dist/backend/cloud/cloud-backend/packages-publish.js +99 -0
  12. package/dist/backend/cloud/cloud-backend/packages-registry.d.ts +6 -0
  13. package/dist/backend/cloud/cloud-backend/packages-registry.js +146 -0
  14. package/dist/backend/cloud/cloud-backend/packages-workspace.d.ts +4 -0
  15. package/dist/backend/cloud/cloud-backend/packages-workspace.js +50 -0
  16. package/dist/backend/cloud/cloud-backend/projects.d.ts +7 -0
  17. package/dist/backend/cloud/cloud-backend/projects.js +122 -0
  18. package/dist/backend/cloud/cloud-backend/state.d.ts +6 -0
  19. package/dist/backend/cloud/cloud-backend/state.js +86 -0
  20. package/dist/backend/cloud/cloud-backend/utils.d.ts +55 -0
  21. package/dist/backend/cloud/cloud-backend/utils.js +56 -0
  22. package/dist/backend/cloud/cloud-backend/workspace-env.d.ts +5 -0
  23. package/dist/backend/cloud/cloud-backend/workspace-env.js +63 -0
  24. package/dist/backend/cloud/cloud-backend/workspaces.d.ts +7 -0
  25. package/dist/backend/cloud/cloud-backend/workspaces.js +124 -0
  26. package/dist/backend/cloud/cloud-backend.d.ts +56 -126
  27. package/dist/backend/cloud/cloud-backend.js +95 -1089
  28. package/dist/backend/cloud/cloud-backend.test.setup.d.ts +13 -0
  29. package/dist/backend/cloud/cloud-backend.test.setup.js +14 -0
  30. package/dist/backend/local.setup.d.ts +10 -0
  31. package/dist/backend/local.setup.js +20 -0
  32. package/dist/commands/executor/start/index.d.ts +1 -11
  33. package/dist/commands/executor/start/index.js +13 -498
  34. package/dist/commands/import-repo/index.js +2 -2
  35. package/dist/lib/env/test.setup.d.ts +7 -0
  36. package/dist/lib/env/test.setup.js +18 -0
  37. package/dist/lib/executor-start/auth.d.ts +2 -0
  38. package/dist/lib/executor-start/auth.js +21 -0
  39. package/dist/lib/executor-start/execute-app-job.d.ts +13 -0
  40. package/dist/lib/executor-start/execute-app-job.js +146 -0
  41. package/dist/lib/executor-start/execute-deploy-job.d.ts +14 -0
  42. package/dist/lib/executor-start/execute-deploy-job.js +64 -0
  43. package/dist/lib/executor-start/execute-init-job.d.ts +14 -0
  44. package/dist/lib/executor-start/execute-init-job.js +135 -0
  45. package/dist/lib/executor-start/format.d.ts +13 -0
  46. package/dist/lib/executor-start/format.js +22 -0
  47. package/dist/lib/executor-start/job-dispatch.d.ts +15 -0
  48. package/dist/lib/executor-start/job-dispatch.js +89 -0
  49. package/dist/lib/package/index.d.ts +4 -4
  50. package/oclif.manifest.json +52 -52
  51. package/package.json +1 -1
@@ -1,1137 +1,143 @@
1
- import { resolveWorkspaceName } from '../../lib/workspace-utils.js';
1
+ import * as appsDeploy from './cloud-backend/apps-deploy.js';
2
+ import * as appsVersions from './cloud-backend/apps-versions.js';
3
+ import * as executorJobs from './cloud-backend/executor-jobs.js';
4
+ import * as misc from './cloud-backend/misc.js';
5
+ import * as packagesPublish from './cloud-backend/packages-publish.js';
6
+ import * as packagesRegistry from './cloud-backend/packages-registry.js';
7
+ import * as packagesWorkspace from './cloud-backend/packages-workspace.js';
8
+ import * as projects from './cloud-backend/projects.js';
9
+ import * as state from './cloud-backend/state.js';
10
+ import * as workspaceEnv from './cloud-backend/workspace-env.js';
11
+ import * as workspaces from './cloud-backend/workspaces.js';
2
12
  export class CloudBackend {
3
13
  config;
4
14
  constructor(config) {
5
15
  this.config = config;
6
16
  }
7
- async addPackageToWorkspace(input) {
8
- const formData = new FormData();
9
- formData.append('package', input.package);
10
- formData.append('infra', input.infra);
11
- formData.append('env', JSON.stringify(input.env));
12
- if (input.parameters) {
13
- formData.append('parameters', JSON.stringify(input.parameters));
14
- }
15
- if (input.version) {
16
- formData.append('version', input.version);
17
- }
18
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/packages`, {
19
- body: formData,
20
- headers: {
21
- 'Authorization': `Bearer ${this.config.accessToken}`,
22
- },
23
- method: 'POST',
24
- });
25
- if (!response.ok) {
26
- return {
27
- reason: JSON.stringify(await this.safeResponseJson(response)),
28
- success: false,
29
- };
30
- }
31
- const result = await response.json();
32
- return {
33
- success: true,
34
- workspace: this.convertWorkspace(result.workspace),
35
- };
17
+ addPackageToWorkspace(input) {
18
+ return packagesWorkspace.addPackageToWorkspace(this.config, input);
36
19
  }
37
- async createWorkspace(input) {
38
- const formData = new FormData();
39
- formData.append('name', input.name);
40
- if (input.mirrorOf) {
41
- formData.append('mirrorOf', input.mirrorOf);
42
- }
43
- if (input.profile) {
44
- formData.append('profile', input.profile);
45
- }
46
- if (input.isDeploy !== undefined) {
47
- formData.append('isDeploy', String(input.isDeploy));
48
- }
49
- const response = await fetch(`${this.config.url}/api/workspaces`, {
50
- body: formData,
51
- headers: {
52
- 'Authorization': `Bearer ${this.config.accessToken}`,
53
- },
54
- method: 'POST',
55
- });
56
- if (!response.ok) {
57
- return {
58
- reason: JSON.stringify(await this.safeResponseJson(response)),
59
- success: false,
60
- };
61
- }
62
- const result = await response.json();
63
- return {
64
- isNew: true,
65
- success: true,
66
- workspace: this.convertWorkspace(result.workspace),
67
- };
20
+ createWorkspace(input) {
21
+ return workspaces.createWorkspace(this.config, input);
68
22
  }
69
- async deleteState(input) {
70
- const response = await fetch(`${this.config.url}/api/projects/${encodeURIComponent(input.project)}/state/${encodeURIComponent(input.workspace)}`, {
71
- headers: {
72
- 'Authorization': `Bearer ${this.config.accessToken}`,
73
- },
74
- method: 'DELETE',
75
- });
76
- if (!response.ok) {
77
- return {
78
- reason: JSON.stringify(await this.safeResponseJson(response)),
79
- success: false,
80
- };
81
- }
82
- return {
83
- message: `State for project ${input.project} in workspace ${input.workspace} deleted successfully`,
84
- success: true,
85
- };
23
+ deleteState(input) {
24
+ return state.deleteState(this.config, input);
86
25
  }
87
- async deleteWorkspace(input) {
88
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.name)}`, {
89
- headers: {
90
- 'Authorization': `Bearer ${this.config.accessToken}`,
91
- },
92
- method: 'DELETE',
93
- });
94
- if (!response.ok) {
95
- return {
96
- reason: JSON.stringify(await this.safeResponseJson(response)),
97
- success: false,
98
- };
99
- }
100
- return {
101
- success: true,
102
- };
26
+ deleteWorkspace(input) {
27
+ return workspaces.deleteWorkspace(this.config, input);
103
28
  }
104
- async deployApp(input) {
105
- const body = {
106
- workspace: input.workspace,
107
- };
108
- if (input.version)
109
- body.version = input.version;
110
- if (input.parameters)
111
- body.parameters = input.parameters;
112
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(input.name)}/deployments`, {
113
- body: JSON.stringify(body),
114
- headers: {
115
- 'Authorization': `Bearer ${this.config.accessToken}`,
116
- 'Content-Type': 'application/json',
117
- },
118
- method: 'POST',
119
- });
120
- if (!response.ok) {
121
- const error = await this.safeResponseJson(response);
122
- return { reason: error.error || `Failed to deploy app (status ${response.status})`, success: false };
123
- }
124
- const result = await response.json();
125
- return { deploymentId: result.deploymentId, jobId: result.jobId, success: true };
29
+ deployApp(input) {
30
+ return appsDeploy.deployApp(this.config, input);
126
31
  }
127
- async destroyApp(input) {
128
- const body = input.parameters ? JSON.stringify({ parameters: input.parameters }) : undefined;
129
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(input.name)}/deployments/${encodeURIComponent(input.workspace)}`, {
130
- body,
131
- headers: {
132
- 'Authorization': `Bearer ${this.config.accessToken}`,
133
- ...(body ? { 'Content-Type': 'application/json' } : {}),
134
- },
135
- method: 'DELETE',
136
- });
137
- if (!response.ok) {
138
- const error = await this.safeResponseJson(response);
139
- return { reason: error.error || `Failed to destroy app (status ${response.status})`, success: false };
140
- }
141
- const result = await response.json();
142
- return { jobId: result.jobId, success: true };
32
+ destroyApp(input) {
33
+ return appsDeploy.destroyApp(this.config, input);
143
34
  }
144
- async exportBackend() {
145
- const response = await fetch(`${this.config.url}/api/export`, {
146
- headers: {
147
- 'Authorization': `Bearer ${this.config.accessToken}`,
148
- },
149
- method: 'GET',
150
- });
151
- if (!response.ok) {
152
- return {
153
- reason: JSON.stringify(await this.safeResponseJson(response)),
154
- success: false,
155
- };
156
- }
157
- const result = await response.json();
158
- if (!result.success) {
159
- return {
160
- reason: JSON.stringify(result),
161
- success: false,
162
- };
163
- }
164
- return {
165
- data: JSON.stringify(result.data),
166
- success: true,
167
- };
35
+ exportBackend() {
36
+ return misc.exportBackend(this.config);
168
37
  }
169
- async generateExecutorToken(input) {
170
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/executor-token`, {
171
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
172
- method: 'POST',
173
- });
174
- if (!response.ok) {
175
- const error = await this.safeResponseJson(response);
176
- return { reason: error.error || 'Failed to generate executor token', success: false };
177
- }
178
- const result = await response.json();
179
- return { expiresAt: result.expiresAt, success: true, token: result.token };
38
+ generateExecutorToken(input) {
39
+ return executorJobs.generateExecutorToken(this.config, input);
180
40
  }
181
- async getApp(name) {
182
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(name)}`, {
183
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
184
- method: 'GET',
185
- });
186
- if (!response.ok) {
187
- const error = await this.safeResponseJson(response);
188
- let errorMessage = error.error || `Failed to get app (status ${response.status})`;
189
- if (response.status === 404) {
190
- errorMessage = `App '${name}' not found`;
191
- }
192
- return { reason: errorMessage, success: false };
193
- }
194
- const result = await response.json();
195
- return { app: result.app, success: true };
41
+ getApp(name) {
42
+ return appsVersions.getApp(this.config, name);
196
43
  }
197
- async getAppDeployment(input) {
198
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(input.name)}/deployments/${encodeURIComponent(input.workspace)}`, {
199
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
200
- method: 'GET',
201
- });
202
- if (!response.ok) {
203
- const error = await this.safeResponseJson(response);
204
- let errorMessage = error.error || `Failed to get app deployment (status ${response.status})`;
205
- if (response.status === 404) {
206
- errorMessage = `Deployment for app '${input.name}' on workspace '${input.workspace}' not found`;
207
- }
208
- return { reason: errorMessage, success: false };
209
- }
210
- const result = await response.json();
211
- return { deployment: result.deployment, success: true };
44
+ getAppDeployment(input) {
45
+ return appsDeploy.getAppDeployment(this.config, input);
212
46
  }
213
- async getAppVersion(input) {
214
- const url = input.version
215
- ? `${this.config.url}/api/apps/${encodeURIComponent(input.name)}/versions/${encodeURIComponent(input.version)}`
216
- : `${this.config.url}/api/apps/${encodeURIComponent(input.name)}/versions/latest`;
217
- const response = await fetch(url, {
218
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
219
- method: 'GET',
220
- });
221
- if (!response.ok) {
222
- const error = await this.safeResponseJson(response);
223
- let errorMessage = error.error || `Failed to get app version (status ${response.status})`;
224
- if (response.status === 404) {
225
- errorMessage = input.version
226
- ? `App version '${input.name}@${input.version}' not found`
227
- : `App '${input.name}' not found`;
228
- }
229
- return { reason: errorMessage, success: false };
230
- }
231
- const result = await response.json();
232
- return { appVersion: result.appVersion ?? result.version, success: true };
47
+ getAppVersion(input) {
48
+ return appsVersions.getAppVersion(this.config, input);
233
49
  }
234
- async getExecutorJobStatus(input) {
235
- const url = new URL(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/jobs/${encodeURIComponent(input.jobId)}`);
236
- if (input.poll) {
237
- url.searchParams.set('poll', 'true');
238
- }
239
- if (input.lastStatus) {
240
- url.searchParams.set('lastStatus', input.lastStatus);
241
- }
242
- const response = await fetch(url.toString(), {
243
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
244
- method: 'GET',
245
- });
246
- if (!response.ok) {
247
- if (response.status === 401) {
248
- return { reason: 'Unauthorized', success: false };
249
- }
250
- const error = await this.safeResponseJson(response);
251
- return { reason: error.error || 'Failed to get job status', success: false };
252
- }
253
- const result = await response.json();
254
- return { job: result.job, success: true };
50
+ getExecutorJobStatus(input) {
51
+ return executorJobs.getExecutorJobStatus(this.config, input);
255
52
  }
256
- async getPackageByVersion(name, version) {
257
- const response = await fetch(`${this.config.url}/api/registry/packages/${encodeURIComponent(name)}/${encodeURIComponent(version)}`, {
258
- headers: {
259
- 'Authorization': `Bearer ${this.config.accessToken}`,
260
- },
261
- method: 'GET',
262
- });
263
- const result = await this.safeResponseJson(response);
264
- if (!response.ok) {
265
- // Handle error responses
266
- let errorMessage = result.error || 'Failed to get package';
267
- switch (response.status) {
268
- case 401: {
269
- errorMessage = 'Authentication failed. Please run `hereya login` to refresh your credentials.';
270
- break;
271
- }
272
- case 403: {
273
- errorMessage = `Access denied: ${errorMessage}`;
274
- break;
275
- }
276
- case 404: {
277
- errorMessage = `Package '${name}@${version}' not found`;
278
- break;
279
- }
280
- }
281
- return {
282
- reason: errorMessage,
283
- success: false,
284
- };
285
- }
286
- return {
287
- package: result.package,
288
- success: true,
289
- };
53
+ getPackageByVersion(name, version) {
54
+ return packagesRegistry.getPackageByVersion(this.config, name, version);
290
55
  }
291
- async getPackageLatest(name) {
292
- const response = await fetch(`${this.config.url}/api/registry/packages/${encodeURIComponent(name)}`, {
293
- headers: {
294
- 'Authorization': `Bearer ${this.config.accessToken}`,
295
- },
296
- method: 'GET',
297
- });
298
- const result = await this.safeResponseJson(response);
299
- if (!response.ok) {
300
- // Handle error responses
301
- let errorMessage = result.error || 'Failed to get package';
302
- switch (response.status) {
303
- case 401: {
304
- errorMessage = 'Authentication failed. Please run `hereya login` to refresh your credentials.';
305
- break;
306
- }
307
- case 403: {
308
- errorMessage = `Access denied: ${errorMessage}`;
309
- break;
310
- }
311
- case 404: {
312
- errorMessage = `Package '${name}' not found`;
313
- break;
314
- }
315
- }
316
- return {
317
- reason: errorMessage,
318
- success: false,
319
- };
320
- }
321
- return {
322
- package: result.package,
323
- success: true,
324
- };
56
+ getPackageLatest(name) {
57
+ return packagesRegistry.getPackageLatest(this.config, name);
325
58
  }
326
- async getProjectMetadata(input) {
327
- const response = await fetch(`${this.config.url}/api/projects/${encodeURIComponent(input.project)}/metadata`, {
328
- headers: {
329
- 'Authorization': `Bearer ${this.config.accessToken}`,
330
- },
331
- method: 'GET',
332
- });
333
- if (!response.ok) {
334
- return {
335
- found: false,
336
- reason: JSON.stringify(await this.safeResponseJson(response)),
337
- };
338
- }
339
- const result = await this.safeResponseJson(response);
340
- if (!result.metadata) {
341
- return {
342
- found: false,
343
- };
344
- }
345
- return {
346
- defaultWorkspace: result.defaultWorkspace,
347
- found: true,
348
- metadata: result.metadata,
349
- };
59
+ getProjectMetadata(input) {
60
+ return projects.getProjectMetadata(this.config, input);
350
61
  }
351
- async getProvisioningId(input) {
352
- const formData = new FormData();
353
- formData.append('packageCanonicalName', input.packageCanonicalName);
354
- formData.append('logicalId', input.logicalId);
355
- if (input.app) {
356
- formData.append('app', input.app);
357
- }
358
- if (input.project) {
359
- formData.append('project', input.project);
360
- }
361
- if (input.workspace) {
362
- formData.append('workspace', input.workspace);
363
- }
364
- const response = await fetch(`${this.config.url}/api/provisioning-id`, {
365
- body: formData,
366
- headers: {
367
- 'Authorization': `Bearer ${this.config.accessToken}`,
368
- },
369
- method: 'POST',
370
- });
371
- if (!response.ok) {
372
- return {
373
- reason: JSON.stringify(await this.safeResponseJson(response)),
374
- success: false,
375
- };
376
- }
377
- const result = await response.json();
378
- return {
379
- id: result.provisioningId.id,
380
- success: true,
381
- };
62
+ getProvisioningId(input) {
63
+ return misc.getProvisioningId(this.config, input);
382
64
  }
383
- async getState(input) {
384
- const url = new URL(`${this.config.url}/api/projects/${encodeURIComponent(input.project)}/state`);
385
- url.searchParams.append('workspace', input.workspace);
386
- const response = await fetch(url, {
387
- headers: {
388
- 'Authorization': `Bearer ${this.config.accessToken}`,
389
- },
390
- method: 'GET',
391
- });
392
- if (!response.ok) {
393
- return {
394
- found: false,
395
- reason: JSON.stringify(await this.safeResponseJson(response)),
396
- };
397
- }
398
- const result = await response.json();
399
- if (!result.success) {
400
- return {
401
- found: false,
402
- };
403
- }
404
- const deploy = {};
405
- for (const item of result.config.deploy) {
406
- deploy[item.name] = { version: item.version };
407
- }
408
- const packages = {};
409
- for (const item of result.config.packages) {
410
- packages[item.name] = { version: item.version };
411
- }
412
- return {
413
- config: {
414
- deploy,
415
- packages,
416
- project: input.project,
417
- workspace: input.workspace,
418
- },
419
- found: true,
420
- };
65
+ getState(input) {
66
+ return state.getState(this.config, input);
421
67
  }
422
- async getWorkspace(name) {
423
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(name)}`, {
424
- headers: {
425
- 'Authorization': `Bearer ${this.config.accessToken}`,
426
- },
427
- method: 'GET',
428
- });
429
- // Handle 404 as "not found" rather than error
430
- if (response.status === 404) {
431
- return {
432
- found: false,
433
- };
434
- }
435
- if (!response.ok) {
436
- const error = await this.safeResponseJson(response);
437
- return {
438
- error: JSON.stringify(error),
439
- found: true,
440
- hasError: true,
441
- };
442
- }
443
- const result = await response.json();
444
- return {
445
- found: true,
446
- hasError: false,
447
- workspace: this.convertWorkspace(result.workspace),
448
- };
68
+ getWorkspace(name) {
69
+ return workspaces.getWorkspace(this.config, name);
449
70
  }
450
- async getWorkspaceEnv(input) {
451
- // Apply org prefix if project has one and workspace doesn't
452
- const workspaceName = resolveWorkspaceName(input.workspace, input.project);
453
- const workspace$ = await this.getWorkspace(workspaceName);
454
- if (!workspace$.found) {
455
- return {
456
- reason: `Workspace ${input.workspace} not found`,
457
- success: false,
458
- };
459
- }
460
- if (workspace$.hasError) {
461
- return {
462
- reason: workspace$.error,
463
- success: false,
464
- };
465
- }
466
- return {
467
- env: workspace$.workspace.env ?? {},
468
- success: true,
469
- };
71
+ getWorkspaceEnv(input) {
72
+ return workspaceEnv.getWorkspaceEnv(this.config, input);
470
73
  }
471
- async importBackend(input) {
472
- const formData = new FormData();
473
- formData.append('data', input.data);
474
- const response = await fetch(`${this.config.url}/api/import`, {
475
- body: formData,
476
- headers: {
477
- 'Authorization': `Bearer ${this.config.accessToken}`,
478
- },
479
- method: 'POST',
480
- });
481
- if (!response.ok) {
482
- return {
483
- reason: JSON.stringify(await this.safeResponseJson(response)),
484
- success: false,
485
- };
486
- }
487
- return {
488
- success: true,
489
- };
74
+ importBackend(input) {
75
+ return misc.importBackend(this.config, input);
490
76
  }
491
- async importProject(input) {
492
- const response = await fetch(`${this.config.url}/api/projects/${encodeURIComponent(input.project)}/import`, {
493
- body: JSON.stringify({ repoUrl: input.repoUrl, workspace: input.workspace }),
494
- headers: {
495
- 'Authorization': `Bearer ${this.config.accessToken}`,
496
- 'Content-Type': 'application/json',
497
- },
498
- method: 'POST',
499
- });
500
- const result = await this.safeResponseJson(response);
501
- if (!response.ok) {
502
- const reason = typeof result?.error === 'string'
503
- ? result.error
504
- : (result?.errors && typeof result.errors === 'object'
505
- ? JSON.stringify(result.errors)
506
- : JSON.stringify(result));
507
- return {
508
- ...(typeof result?.code === 'string' ? { code: result.code } : {}),
509
- reason,
510
- success: false,
511
- };
512
- }
513
- if (!result?.success || !result?.project) {
514
- return {
515
- reason: typeof result?.error === 'string' ? result.error : 'Unexpected response from server',
516
- success: false,
517
- };
518
- }
519
- return {
520
- project: {
521
- defaultWorkspace: result.project.defaultWorkspace,
522
- name: result.project.name,
523
- },
524
- success: true,
525
- };
77
+ importProject(input) {
78
+ return projects.importProject(this.config, input);
526
79
  }
527
- async init(input) {
528
- const formData = new FormData();
529
- formData.append('name', input.project);
530
- formData.append('workspace', input.workspace);
531
- const response = await fetch(`${this.config.url}/api/projects`, {
532
- body: formData,
533
- headers: {
534
- 'Authorization': `Bearer ${this.config.accessToken}`,
535
- },
536
- method: 'POST',
537
- });
538
- if (!response.ok) {
539
- throw new Error(JSON.stringify(await this.safeResponseJson(response)));
540
- }
541
- const result = await response.json();
542
- return {
543
- project: {
544
- id: result.project.id,
545
- name: result.project.name,
546
- },
547
- workspace: {
548
- id: result.project.defaultWorkspace.id,
549
- name: result.project.defaultWorkspace.name,
550
- },
551
- };
80
+ init(input) {
81
+ return projects.init(this.config, input);
552
82
  }
553
- async listAppDeployments(name) {
554
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(name)}/deployments`, {
555
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
556
- method: 'GET',
557
- });
558
- if (!response.ok) {
559
- const error = await this.safeResponseJson(response);
560
- return { reason: error.error || `Failed to list app deployments (status ${response.status})`, success: false };
561
- }
562
- const result = await response.json();
563
- return { deployments: result.deployments || [], success: true };
83
+ listAppDeployments(name) {
84
+ return appsDeploy.listAppDeployments(this.config, name);
564
85
  }
565
- async listApps() {
566
- const response = await fetch(`${this.config.url}/api/apps`, {
567
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
568
- method: 'GET',
569
- });
570
- if (!response.ok) {
571
- const error = await this.safeResponseJson(response);
572
- return { reason: error.error || `Failed to list apps (status ${response.status})`, success: false };
573
- }
574
- const result = await response.json();
575
- return { apps: result.apps || [], success: true };
86
+ listApps() {
87
+ return appsVersions.listApps(this.config);
576
88
  }
577
- async listAppVersions(name) {
578
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(name)}/versions`, {
579
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
580
- method: 'GET',
581
- });
582
- if (!response.ok) {
583
- const error = await this.safeResponseJson(response);
584
- return { reason: error.error || `Failed to list app versions (status ${response.status})`, success: false };
585
- }
586
- const result = await response.json();
587
- return { success: true, versions: result.versions || [] };
89
+ listAppVersions(name) {
90
+ return appsVersions.listAppVersions(this.config, name);
588
91
  }
589
- async listPackageVersions(name) {
590
- const response = await fetch(`${this.config.url}/api/registry/packages/${encodeURIComponent(name)}?versions=all`, {
591
- headers: {
592
- 'Authorization': `Bearer ${this.config.accessToken}`,
593
- },
594
- method: 'GET',
595
- });
596
- const result = await this.safeResponseJson(response);
597
- if (!response.ok) {
598
- // Handle error responses
599
- let errorMessage = result.error || 'Failed to list package versions';
600
- switch (response.status) {
601
- case 401: {
602
- errorMessage = 'Authentication failed. Please run `hereya login` to refresh your credentials.';
603
- break;
604
- }
605
- case 403: {
606
- errorMessage = `Access denied: ${errorMessage}`;
607
- break;
608
- }
609
- case 404: {
610
- errorMessage = `Package '${name}' not found`;
611
- break;
612
- }
613
- }
614
- return {
615
- reason: errorMessage,
616
- success: false,
617
- };
618
- }
619
- return {
620
- packages: result.packages || [],
621
- success: true,
622
- };
92
+ listPackageVersions(name) {
93
+ return packagesRegistry.listPackageVersions(this.config, name);
623
94
  }
624
- async listProjects() {
625
- const response = await fetch(`${this.config.url}/api/projects`, {
626
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
627
- method: 'GET',
628
- });
629
- if (!response.ok) {
630
- throw new Error(JSON.stringify(await this.safeResponseJson(response)));
631
- }
632
- const result = await response.json();
633
- return (result.projects ?? []).map((p) => ({
634
- defaultWorkspace: p.defaultWorkspace?.name ?? '-',
635
- name: p.name,
636
- }));
95
+ listProjects() {
96
+ return projects.listProjects(this.config);
637
97
  }
638
- async listWorkspaces(input) {
639
- const url = new URL(`${this.config.url}/api/workspaces`);
640
- if (input?.org) {
641
- url.searchParams.set('org', input.org);
642
- }
643
- const response = await fetch(url.toString(), {
644
- headers: {
645
- 'Authorization': `Bearer ${this.config.accessToken}`,
646
- },
647
- method: 'GET',
648
- });
649
- if (!response.ok) {
650
- throw new Error(JSON.stringify(await this.safeResponseJson(response)));
651
- }
652
- const result = await response.json();
653
- return result.workspaces.map((workspace) => workspace.name);
98
+ listWorkspaces(input) {
99
+ return workspaces.listWorkspaces(this.config, input);
654
100
  }
655
- async pollExecutorJobs(input) {
656
- const url = new URL(`${this.config.url}/api/executor/jobs`);
657
- url.searchParams.set('workspace', input.workspace);
658
- if (input.executorId) {
659
- url.searchParams.set('executorId', input.executorId);
660
- }
661
- const response = await fetch(url.toString(), {
662
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
663
- method: 'GET',
664
- });
665
- if (!response.ok) {
666
- if (response.status === 401) {
667
- return { reason: 'Unauthorized', success: false, unauthorized: true };
668
- }
669
- const error = await this.safeResponseJson(response);
670
- return { reason: error.error || 'Failed to poll for jobs', success: false };
671
- }
672
- const result = await response.json();
673
- return { job: result.job, success: true };
101
+ pollExecutorJobs(input) {
102
+ return executorJobs.pollExecutorJobs(this.config, input);
674
103
  }
675
- async publishAppVersion(input) {
676
- const requestBody = {
677
- commit: input.commit,
678
- hereyaYaml: input.hereyaYaml,
679
- repository: input.repository,
680
- sha256: input.sha256,
681
- version: input.version,
682
- };
683
- if (input.description !== undefined)
684
- requestBody.description = input.description;
685
- if (input.visibility !== undefined)
686
- requestBody.visibility = input.visibility;
687
- if (input.parameters !== undefined)
688
- requestBody.parameters = input.parameters;
689
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(input.name)}/versions`, {
690
- body: JSON.stringify(requestBody),
691
- headers: {
692
- 'Authorization': `Bearer ${this.config.accessToken}`,
693
- 'Content-Type': 'application/json',
694
- },
695
- method: 'POST',
696
- });
697
- const result = await this.safeResponseJson(response);
698
- if (!response.ok) {
699
- let errorMessage = result.error || 'Failed to publish app version';
700
- switch (response.status) {
701
- case 401: {
702
- errorMessage = 'Authentication failed. Please run `hereya login` to refresh your credentials.';
703
- break;
704
- }
705
- case 403: {
706
- errorMessage = `Permission denied: ${errorMessage}`;
707
- break;
708
- }
709
- case 409: {
710
- errorMessage = `Conflict: ${errorMessage}`;
711
- break;
712
- }
713
- }
714
- return { reason: errorMessage, success: false };
715
- }
716
- const app = result.appVersion ?? result.app ?? { name: input.name, version: input.version };
717
- return {
718
- app: {
719
- id: app.id,
720
- name: app.name ?? input.name,
721
- version: app.version ?? input.version,
722
- },
723
- success: true,
724
- };
104
+ publishAppVersion(input) {
105
+ return appsVersions.publishAppVersion(this.config, input);
725
106
  }
726
- async publishPackage(input) {
727
- const formData = new FormData();
728
- formData.append('name', input.name);
729
- formData.append('version', input.version);
730
- formData.append('description', input.description);
731
- formData.append('repository', input.repository);
732
- formData.append('commit', input.commit);
733
- formData.append('sha256', input.sha256);
734
- formData.append('iac', input.iac);
735
- formData.append('infra', input.infra);
736
- if (input.doc) {
737
- formData.append('doc', input.doc);
738
- }
739
- if (input.visibility) {
740
- formData.append('visibility', input.visibility);
741
- }
742
- if (input.onDeployPkg) {
743
- formData.append('onDeployPkg', input.onDeployPkg);
744
- }
745
- if (input.onDeployVersion) {
746
- formData.append('onDeployVersion', input.onDeployVersion);
747
- }
748
- const response = await fetch(`${this.config.url}/api/registry/packages`, {
749
- body: formData,
750
- headers: {
751
- 'Authorization': `Bearer ${this.config.accessToken}`,
752
- },
753
- method: 'POST',
754
- });
755
- const result = await this.safeResponseJson(response);
756
- if (!response.ok) {
757
- // Handle validation errors (field-specific errors)
758
- if (result.errors && typeof result.errors === 'object') {
759
- const errorMessages = [];
760
- // Add main error message if present
761
- if (result.error) {
762
- errorMessages.push(result.error, ''); // Empty line for separation
763
- }
764
- // Add field-specific errors
765
- for (const [field, messages] of Object.entries(result.errors)) {
766
- if (Array.isArray(messages)) {
767
- errorMessages.push(`${field}: ${messages.join(', ')}`);
768
- }
769
- }
770
- // Add help message if present
771
- if (result.help) {
772
- errorMessages.push('', `ℹ️ ${result.help}`);
773
- }
774
- return {
775
- reason: errorMessages.join('\n'),
776
- success: false,
777
- };
778
- }
779
- // Handle other errors with more context
780
- const errorMessages = [];
781
- const errorMessage = result.error || 'Unknown error occurred';
782
- // Add main error with context based on errorType or status code
783
- if (result.errorType) {
784
- errorMessages.push(`[${result.errorType}] ${errorMessage}`);
785
- }
786
- else {
787
- // Fallback to status-based context
788
- switch (response.status) {
789
- case 401: {
790
- errorMessages.push('Authentication failed. Please run `hereya login` to refresh your credentials.');
791
- break;
792
- }
793
- case 403: {
794
- errorMessages.push(`Permission denied: ${errorMessage}`);
795
- break;
796
- }
797
- case 409: {
798
- errorMessages.push(`Conflict: ${errorMessage}`);
799
- break;
800
- }
801
- default: {
802
- if (response.status >= 500) {
803
- errorMessages.push(`Server error: ${errorMessage} (status ${response.status})`);
804
- }
805
- else {
806
- errorMessages.push(errorMessage);
807
- }
808
- }
809
- }
810
- }
811
- // Add help message if present
812
- if (result.help) {
813
- errorMessages.push('', `ℹ️ ${result.help}`);
814
- }
815
- // Add package info if present (helpful for debugging)
816
- if (result.package) {
817
- errorMessages.push('', `Package: ${result.package.name}@${result.package.version}`);
818
- }
819
- return {
820
- reason: errorMessages.join('\n'),
821
- success: false,
822
- };
823
- }
824
- return {
825
- package: result.package,
826
- success: true,
827
- };
107
+ publishPackage(input) {
108
+ return packagesPublish.publishPackage(this.config, input);
828
109
  }
829
- async removePackageFromWorkspace(input) {
830
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/packages/${encodeURIComponent(input.package)}`, {
831
- headers: {
832
- 'Authorization': `Bearer ${this.config.accessToken}`,
833
- },
834
- method: 'DELETE',
835
- });
836
- if (!response.ok) {
837
- return {
838
- reason: JSON.stringify(await this.safeResponseJson(response)),
839
- success: false,
840
- };
841
- }
842
- const result = await response.json();
843
- return {
844
- success: true,
845
- workspace: this.convertWorkspace(result.workspace),
846
- };
110
+ removePackageFromWorkspace(input) {
111
+ return packagesWorkspace.removePackageFromWorkspace(this.config, input);
847
112
  }
848
- async revokeExecutorToken(input) {
849
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/executor-token`, {
850
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
851
- method: 'DELETE',
852
- });
853
- if (!response.ok) {
854
- const error = await this.safeResponseJson(response);
855
- return { reason: error.error || 'Failed to revoke executor token', success: false };
856
- }
857
- return { success: true };
113
+ revokeExecutorToken(input) {
114
+ return executorJobs.revokeExecutorToken(this.config, input);
858
115
  }
859
- async saveProjectMetadata(input) {
860
- const formData = new FormData();
861
- formData.append('metadata', JSON.stringify(input.metadata));
862
- const response = await fetch(`${this.config.url}/api/projects/${encodeURIComponent(input.project)}/metadata`, {
863
- body: formData,
864
- headers: {
865
- 'Authorization': `Bearer ${this.config.accessToken}`,
866
- },
867
- method: 'POST',
868
- });
869
- if (!response.ok) {
870
- return {
871
- reason: JSON.stringify(await this.safeResponseJson(response)),
872
- success: false,
873
- };
874
- }
875
- return {
876
- success: true,
877
- };
116
+ saveProjectMetadata(input) {
117
+ return projects.saveProjectMetadata(this.config, input);
878
118
  }
879
- async saveState(config, workspace) {
880
- const formData = new FormData();
881
- if (workspace) {
882
- formData.append('workspace', workspace);
883
- }
884
- if (config.deploy) {
885
- const deployArray = Object.entries(config.deploy).map(([name, { version }]) => ({ name, version })).sort((a, b) => a.name.localeCompare(b.name));
886
- formData.append('deploy', JSON.stringify(deployArray));
887
- }
888
- if (config.packages) {
889
- const packagesArray = Object.entries(config.packages).map(([name, { version }]) => ({ name, version })).sort((a, b) => a.name.localeCompare(b.name));
890
- formData.append('packages', JSON.stringify(packagesArray));
891
- }
892
- const response = await fetch(`${this.config.url}/api/projects/${encodeURIComponent(config.project)}/state`, {
893
- body: formData,
894
- headers: {
895
- 'Authorization': `Bearer ${this.config.accessToken}`,
896
- },
897
- method: 'POST',
898
- });
899
- if (!response.ok) {
900
- throw new Error(JSON.stringify(await this.safeResponseJson(response)));
901
- }
119
+ saveState(config, workspace) {
120
+ return state.saveState(this.config, config, workspace);
902
121
  }
903
- async searchPackages(input) {
904
- const url = new URL(`${this.config.url}/api/registry/packages/search`);
905
- url.searchParams.append('q', input.query);
906
- if (input.limit !== undefined) {
907
- url.searchParams.append('limit', String(input.limit));
908
- }
909
- if (input.offset !== undefined) {
910
- url.searchParams.append('offset', String(input.offset));
911
- }
912
- const response = await fetch(url, {
913
- headers: {
914
- 'Authorization': `Bearer ${this.config.accessToken}`,
915
- },
916
- method: 'GET',
917
- });
918
- const result = await this.safeResponseJson(response);
919
- if (!response.ok) {
920
- let errorMessage = result.error || 'Failed to search packages';
921
- switch (response.status) {
922
- case 400: {
923
- errorMessage = `Invalid search query: ${errorMessage}`;
924
- break;
925
- }
926
- case 401: {
927
- errorMessage = 'Authentication failed. Please run `hereya login` to refresh your credentials.';
928
- break;
929
- }
930
- case 500: {
931
- errorMessage = `Server error: ${errorMessage}`;
932
- break;
933
- }
934
- }
935
- return {
936
- reason: errorMessage,
937
- success: false,
938
- };
939
- }
940
- return {
941
- hasMore: result.hasMore ?? false,
942
- packages: result.packages || [],
943
- success: true,
944
- };
122
+ searchPackages(input) {
123
+ return packagesRegistry.searchPackages(this.config, input);
945
124
  }
946
- async setEnvVar(input) {
947
- const formData = new FormData();
948
- formData.append('key', input.name);
949
- formData.append('value', input.value);
950
- formData.append('infrastructure', input.infrastructure);
951
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/env`, {
952
- body: formData,
953
- headers: {
954
- 'Authorization': `Bearer ${this.config.accessToken}`,
955
- },
956
- method: 'POST',
957
- });
958
- if (!response.ok) {
959
- return {
960
- reason: JSON.stringify(await this.safeResponseJson(response)),
961
- success: false,
962
- };
963
- }
964
- return {
965
- success: true,
966
- };
125
+ setEnvVar(input) {
126
+ return workspaceEnv.setEnvVar(this.config, input);
967
127
  }
968
- async submitExecutorJob(input) {
969
- const formData = new FormData();
970
- formData.append('type', input.type);
971
- formData.append('payload', JSON.stringify(input.payload));
972
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/jobs`, {
973
- body: formData,
974
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
975
- method: 'POST',
976
- });
977
- if (!response.ok) {
978
- if (response.status === 401) {
979
- return { reason: 'Unauthorized', success: false };
980
- }
981
- const error = await this.safeResponseJson(response);
982
- return { reason: error.error || 'Failed to submit executor job', success: false };
983
- }
984
- const result = await response.json();
985
- return { jobId: result.jobId, success: true };
128
+ submitExecutorJob(input) {
129
+ return executorJobs.submitExecutorJob(this.config, input);
986
130
  }
987
- async unsetEnvVar(input) {
988
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/env/${encodeURIComponent(input.name)}`, {
989
- headers: {
990
- 'Authorization': `Bearer ${this.config.accessToken}`,
991
- },
992
- method: 'DELETE',
993
- });
994
- if (!response.ok) {
995
- return {
996
- reason: JSON.stringify(await this.safeResponseJson(response)),
997
- success: false,
998
- };
999
- }
1000
- return {
1001
- success: true,
1002
- };
131
+ unsetEnvVar(input) {
132
+ return workspaceEnv.unsetEnvVar(this.config, input);
1003
133
  }
1004
- async updateAppDeployment(input) {
1005
- const body = { status: input.status };
1006
- if (input.state !== undefined)
1007
- body.state = input.state;
1008
- if (input.env !== undefined)
1009
- body.env = input.env;
1010
- if (input.lastJobId !== undefined)
1011
- body.lastJobId = input.lastJobId;
1012
- const response = await fetch(`${this.config.url}/api/apps/${encodeURIComponent(input.name)}/deployments/${encodeURIComponent(input.workspace)}`, {
1013
- body: JSON.stringify(body),
1014
- headers: {
1015
- 'Authorization': `Bearer ${this.config.accessToken}`,
1016
- 'Content-Type': 'application/json',
1017
- },
1018
- method: 'PATCH',
1019
- });
1020
- if (!response.ok) {
1021
- const error = await this.safeResponseJson(response);
1022
- return { reason: error.error || `Failed to update app deployment (status ${response.status})`, success: false };
1023
- }
1024
- return { success: true };
134
+ updateAppDeployment(input) {
135
+ return appsDeploy.updateAppDeployment(this.config, input);
1025
136
  }
1026
- async updateExecutorJob(input) {
1027
- const formData = new FormData();
1028
- if (input.logs) {
1029
- formData.append('logs', input.logs);
1030
- }
1031
- if (input.status) {
1032
- formData.append('status', input.status);
1033
- }
1034
- if (input.result) {
1035
- formData.append('result', JSON.stringify(input.result));
1036
- }
1037
- const response = await fetch(`${this.config.url}/api/executor/jobs/${encodeURIComponent(input.jobId)}`, {
1038
- body: formData,
1039
- headers: { 'Authorization': `Bearer ${this.config.accessToken}` },
1040
- method: 'PATCH',
1041
- });
1042
- if (!response.ok) {
1043
- if (response.status === 401) {
1044
- return { reason: 'Unauthorized', success: false, unauthorized: true };
1045
- }
1046
- const error = await this.safeResponseJson(response);
1047
- return { reason: error.error || 'Failed to update job', success: false };
1048
- }
1049
- return { success: true };
137
+ updateExecutorJob(input) {
138
+ return executorJobs.updateExecutorJob(this.config, input);
1050
139
  }
1051
- async updateWorkspace(input) {
1052
- const formData = new FormData();
1053
- if (input.profile !== undefined) {
1054
- formData.append('profile', input.profile === null ? '' : input.profile);
1055
- }
1056
- if (input.hasExecutor !== undefined) {
1057
- formData.append('hasExecutor', input.hasExecutor === null ? '' : String(input.hasExecutor));
1058
- }
1059
- if (input.isDeploy !== undefined) {
1060
- formData.append('isDeploy', input.isDeploy === null ? '' : String(input.isDeploy));
1061
- }
1062
- const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.name)}`, {
1063
- body: formData,
1064
- headers: {
1065
- 'Authorization': `Bearer ${this.config.accessToken}`,
1066
- },
1067
- method: 'PATCH',
1068
- });
1069
- if (!response.ok) {
1070
- return {
1071
- reason: JSON.stringify(await this.safeResponseJson(response)),
1072
- success: false,
1073
- };
1074
- }
1075
- const result = await response.json();
1076
- return {
1077
- success: true,
1078
- workspace: this.convertWorkspace(result.workspace),
1079
- };
1080
- }
1081
- convertWorkspace(workspace) {
1082
- const env = {};
1083
- if (workspace.packages) {
1084
- for (const pkg of workspace.packages) {
1085
- if (pkg.env) {
1086
- for (const e of pkg.env) {
1087
- env[e.key] = `${e.infrastructure}:${e.value}`;
1088
- }
1089
- }
1090
- }
1091
- }
1092
- if (workspace.env) {
1093
- for (const e of workspace.env) {
1094
- env[e.key] = `${e.infrastructure}:${e.value}`;
1095
- }
1096
- }
1097
- const packages = {};
1098
- if (workspace.packages) {
1099
- for (const pkg of workspace.packages) {
1100
- packages[pkg.name] = {
1101
- parameters: pkg.parameters,
1102
- version: pkg.version,
1103
- };
1104
- }
1105
- }
1106
- let ownPackages;
1107
- if (workspace.ownPackages) {
1108
- ownPackages = {};
1109
- for (const pkg of workspace.ownPackages) {
1110
- ownPackages[pkg.name] = {
1111
- parameters: pkg.parameters,
1112
- version: pkg.version,
1113
- };
1114
- }
1115
- }
1116
- return {
1117
- env,
1118
- hasExecutor: workspace.hasExecutor ?? undefined,
1119
- id: workspace.id,
1120
- isDeploy: workspace.isDeploy,
1121
- mirrorOf: workspace.mirrorOf?.name,
1122
- name: workspace.name,
1123
- ...(ownPackages === undefined ? {} : { ownPackages }),
1124
- packages,
1125
- profile: workspace.profile === null ? undefined : workspace.profile,
1126
- };
1127
- }
1128
- async safeResponseJson(response) {
1129
- const text = await response.text();
1130
- try {
1131
- return JSON.parse(text);
1132
- }
1133
- catch {
1134
- return { error: text || 'Unknown error' };
1135
- }
140
+ updateWorkspace(input) {
141
+ return workspaces.updateWorkspace(this.config, input);
1136
142
  }
1137
143
  }