hereya-cli 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -23
- package/dist/backend/cloud/cloud-backend.d.ts +27 -0
- package/dist/backend/cloud/cloud-backend.js +334 -0
- package/dist/backend/cloud/login.d.ts +16 -0
- package/dist/backend/cloud/login.js +145 -0
- package/dist/backend/cloud/logout.d.ts +9 -0
- package/dist/backend/cloud/logout.js +12 -0
- package/dist/backend/cloud/utils.d.ts +3 -0
- package/dist/backend/cloud/utils.js +6 -0
- package/dist/backend/common.d.ts +3 -0
- package/dist/backend/config.d.ts +30 -3
- package/dist/backend/config.js +36 -2
- package/dist/backend/file.js +2 -2
- package/dist/backend/index.d.ts +2 -1
- package/dist/backend/index.js +22 -3
- package/dist/backend/secrets.d.ts +5 -0
- package/dist/backend/secrets.js +66 -0
- package/dist/commands/init/index.js +1 -1
- package/dist/commands/login/index.d.ts +9 -0
- package/dist/commands/login/index.js +27 -0
- package/dist/commands/logout/index.d.ts +6 -0
- package/dist/commands/logout/index.js +23 -0
- package/dist/executor/local.js +3 -1
- package/dist/infrastructure/aws.js +2 -17
- package/oclif.manifest.json +56 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ $ npm install -g hereya-cli
|
|
|
20
20
|
$ hereya COMMAND
|
|
21
21
|
running command...
|
|
22
22
|
$ hereya (--version)
|
|
23
|
-
hereya-cli/0.
|
|
23
|
+
hereya-cli/0.35.0 linux-x64 node-v22.15.0
|
|
24
24
|
$ hereya --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ hereya COMMAND
|
|
@@ -39,6 +39,8 @@ USAGE
|
|
|
39
39
|
* [`hereya env set [NAME]`](#hereya-env-set-name)
|
|
40
40
|
* [`hereya help [COMMAND]`](#hereya-help-command)
|
|
41
41
|
* [`hereya init PROJECT`](#hereya-init-project)
|
|
42
|
+
* [`hereya login URL`](#hereya-login-url)
|
|
43
|
+
* [`hereya logout`](#hereya-logout)
|
|
42
44
|
* [`hereya remove PACKAGE`](#hereya-remove-package)
|
|
43
45
|
* [`hereya run CMD`](#hereya-run-cmd)
|
|
44
46
|
* [`hereya unbootstrap INFRASTRUCTURETYPE`](#hereya-unbootstrap-infrastructuretype)
|
|
@@ -84,7 +86,7 @@ EXAMPLES
|
|
|
84
86
|
$ hereya add cloudy/docker_postgres
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
_See code: [src/commands/add/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
89
|
+
_See code: [src/commands/add/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/add/index.ts)_
|
|
88
90
|
|
|
89
91
|
## `hereya bootstrap INFRASTRUCTURETYPE`
|
|
90
92
|
|
|
@@ -109,7 +111,7 @@ EXAMPLES
|
|
|
109
111
|
$ hereya bootstrap local
|
|
110
112
|
```
|
|
111
113
|
|
|
112
|
-
_See code: [src/commands/bootstrap/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
114
|
+
_See code: [src/commands/bootstrap/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/bootstrap/index.ts)_
|
|
113
115
|
|
|
114
116
|
## `hereya config get-backend`
|
|
115
117
|
|
|
@@ -126,7 +128,7 @@ EXAMPLES
|
|
|
126
128
|
$ hereya config get-backend
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
_See code: [src/commands/config/get-backend/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
131
|
+
_See code: [src/commands/config/get-backend/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/config/get-backend/index.ts)_
|
|
130
132
|
|
|
131
133
|
## `hereya config use-backend TYPE`
|
|
132
134
|
|
|
@@ -148,7 +150,7 @@ EXAMPLES
|
|
|
148
150
|
$ hereya config use-backend local
|
|
149
151
|
```
|
|
150
152
|
|
|
151
|
-
_See code: [src/commands/config/use-backend/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
153
|
+
_See code: [src/commands/config/use-backend/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/config/use-backend/index.ts)_
|
|
152
154
|
|
|
153
155
|
## `hereya deploy`
|
|
154
156
|
|
|
@@ -173,7 +175,7 @@ EXAMPLES
|
|
|
173
175
|
$ hereya deploy
|
|
174
176
|
```
|
|
175
177
|
|
|
176
|
-
_See code: [src/commands/deploy/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
178
|
+
_See code: [src/commands/deploy/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/deploy/index.ts)_
|
|
177
179
|
|
|
178
180
|
## `hereya down`
|
|
179
181
|
|
|
@@ -200,7 +202,7 @@ EXAMPLES
|
|
|
200
202
|
$ hereya down
|
|
201
203
|
```
|
|
202
204
|
|
|
203
|
-
_See code: [src/commands/down/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
205
|
+
_See code: [src/commands/down/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/down/index.ts)_
|
|
204
206
|
|
|
205
207
|
## `hereya env [NAME]`
|
|
206
208
|
|
|
@@ -231,7 +233,7 @@ EXAMPLES
|
|
|
231
233
|
$ hereya env -w dev -l
|
|
232
234
|
```
|
|
233
235
|
|
|
234
|
-
_See code: [src/commands/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
236
|
+
_See code: [src/commands/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/env/index.ts)_
|
|
235
237
|
|
|
236
238
|
## `hereya env set [NAME]`
|
|
237
239
|
|
|
@@ -258,7 +260,7 @@ EXAMPLES
|
|
|
258
260
|
$ hereya env set FOO -v bar -w dev
|
|
259
261
|
```
|
|
260
262
|
|
|
261
|
-
_See code: [src/commands/env/set/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
263
|
+
_See code: [src/commands/env/set/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/env/set/index.ts)_
|
|
262
264
|
|
|
263
265
|
## `hereya help [COMMAND]`
|
|
264
266
|
|
|
@@ -304,7 +306,46 @@ EXAMPLES
|
|
|
304
306
|
$ hereya init myProject -w=defaultWorkspace --chdir=./myProject
|
|
305
307
|
```
|
|
306
308
|
|
|
307
|
-
_See code: [src/commands/init/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
309
|
+
_See code: [src/commands/init/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/init/index.ts)_
|
|
310
|
+
|
|
311
|
+
## `hereya login URL`
|
|
312
|
+
|
|
313
|
+
Login to the Hereya Cloud backend
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
USAGE
|
|
317
|
+
$ hereya login URL
|
|
318
|
+
|
|
319
|
+
ARGUMENTS
|
|
320
|
+
URL URL of the Hereya Cloud backend
|
|
321
|
+
|
|
322
|
+
DESCRIPTION
|
|
323
|
+
Login to the Hereya Cloud backend
|
|
324
|
+
|
|
325
|
+
EXAMPLES
|
|
326
|
+
$ hereya login https://cloud.hereya.dev
|
|
327
|
+
|
|
328
|
+
$ hereya login http://localhost:5173
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
_See code: [src/commands/login/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/login/index.ts)_
|
|
332
|
+
|
|
333
|
+
## `hereya logout`
|
|
334
|
+
|
|
335
|
+
Logout from Hereya Cloud
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
USAGE
|
|
339
|
+
$ hereya logout
|
|
340
|
+
|
|
341
|
+
DESCRIPTION
|
|
342
|
+
Logout from Hereya Cloud
|
|
343
|
+
|
|
344
|
+
EXAMPLES
|
|
345
|
+
$ hereya logout
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
_See code: [src/commands/logout/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/logout/index.ts)_
|
|
308
349
|
|
|
309
350
|
## `hereya remove PACKAGE`
|
|
310
351
|
|
|
@@ -331,7 +372,7 @@ EXAMPLES
|
|
|
331
372
|
$ hereya remove cloudy/docker_postgres
|
|
332
373
|
```
|
|
333
374
|
|
|
334
|
-
_See code: [src/commands/remove/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
375
|
+
_See code: [src/commands/remove/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/remove/index.ts)_
|
|
335
376
|
|
|
336
377
|
## `hereya run CMD`
|
|
337
378
|
|
|
@@ -357,7 +398,7 @@ EXAMPLES
|
|
|
357
398
|
$ hereya run -w uat -- node index.js
|
|
358
399
|
```
|
|
359
400
|
|
|
360
|
-
_See code: [src/commands/run/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
401
|
+
_See code: [src/commands/run/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/run/index.ts)_
|
|
361
402
|
|
|
362
403
|
## `hereya unbootstrap INFRASTRUCTURETYPE`
|
|
363
404
|
|
|
@@ -382,7 +423,7 @@ EXAMPLES
|
|
|
382
423
|
$ hereya unbootstrap local
|
|
383
424
|
```
|
|
384
425
|
|
|
385
|
-
_See code: [src/commands/unbootstrap/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
426
|
+
_See code: [src/commands/unbootstrap/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/unbootstrap/index.ts)_
|
|
386
427
|
|
|
387
428
|
## `hereya undeploy`
|
|
388
429
|
|
|
@@ -407,7 +448,7 @@ EXAMPLES
|
|
|
407
448
|
$ hereya undeploy
|
|
408
449
|
```
|
|
409
450
|
|
|
410
|
-
_See code: [src/commands/undeploy/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
451
|
+
_See code: [src/commands/undeploy/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/undeploy/index.ts)_
|
|
411
452
|
|
|
412
453
|
## `hereya up`
|
|
413
454
|
|
|
@@ -434,7 +475,7 @@ EXAMPLES
|
|
|
434
475
|
$ hereya up
|
|
435
476
|
```
|
|
436
477
|
|
|
437
|
-
_See code: [src/commands/up/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
478
|
+
_See code: [src/commands/up/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/up/index.ts)_
|
|
438
479
|
|
|
439
480
|
## `hereya workspace create NAME`
|
|
440
481
|
|
|
@@ -457,7 +498,7 @@ EXAMPLES
|
|
|
457
498
|
$ hereya workspace create dev
|
|
458
499
|
```
|
|
459
500
|
|
|
460
|
-
_See code: [src/commands/workspace/create/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
501
|
+
_See code: [src/commands/workspace/create/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/create/index.ts)_
|
|
461
502
|
|
|
462
503
|
## `hereya workspace delete NAME`
|
|
463
504
|
|
|
@@ -477,7 +518,7 @@ EXAMPLES
|
|
|
477
518
|
$ hereya workspace delete dev
|
|
478
519
|
```
|
|
479
520
|
|
|
480
|
-
_See code: [src/commands/workspace/delete/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
521
|
+
_See code: [src/commands/workspace/delete/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/delete/index.ts)_
|
|
481
522
|
|
|
482
523
|
## `hereya workspace env [NAME]`
|
|
483
524
|
|
|
@@ -503,7 +544,7 @@ EXAMPLES
|
|
|
503
544
|
$ hereya workspace env myEnv -w dev
|
|
504
545
|
```
|
|
505
546
|
|
|
506
|
-
_See code: [src/commands/workspace/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
547
|
+
_See code: [src/commands/workspace/env/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/env/index.ts)_
|
|
507
548
|
|
|
508
549
|
## `hereya workspace env set`
|
|
509
550
|
|
|
@@ -527,7 +568,7 @@ EXAMPLES
|
|
|
527
568
|
$ hereya workspace env set -w my-workspace -n myVar -v my-value -i aws -s
|
|
528
569
|
```
|
|
529
570
|
|
|
530
|
-
_See code: [src/commands/workspace/env/set/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
571
|
+
_See code: [src/commands/workspace/env/set/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/env/set/index.ts)_
|
|
531
572
|
|
|
532
573
|
## `hereya workspace env unset`
|
|
533
574
|
|
|
@@ -548,7 +589,7 @@ EXAMPLES
|
|
|
548
589
|
$ hereya workspace env unset -w my-workspace -n myVar
|
|
549
590
|
```
|
|
550
591
|
|
|
551
|
-
_See code: [src/commands/workspace/env/unset/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
592
|
+
_See code: [src/commands/workspace/env/unset/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/env/unset/index.ts)_
|
|
552
593
|
|
|
553
594
|
## `hereya workspace install PACKAGE`
|
|
554
595
|
|
|
@@ -575,7 +616,7 @@ EXAMPLES
|
|
|
575
616
|
$ hereya workspace install hereya/aws-cognito
|
|
576
617
|
```
|
|
577
618
|
|
|
578
|
-
_See code: [src/commands/workspace/install/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
619
|
+
_See code: [src/commands/workspace/install/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/install/index.ts)_
|
|
579
620
|
|
|
580
621
|
## `hereya workspace list`
|
|
581
622
|
|
|
@@ -592,7 +633,7 @@ EXAMPLES
|
|
|
592
633
|
$ hereya workspace list
|
|
593
634
|
```
|
|
594
635
|
|
|
595
|
-
_See code: [src/commands/workspace/list/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
636
|
+
_See code: [src/commands/workspace/list/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/list/index.ts)_
|
|
596
637
|
|
|
597
638
|
## `hereya workspace uninstall PACKAGE`
|
|
598
639
|
|
|
@@ -619,5 +660,5 @@ EXAMPLES
|
|
|
619
660
|
$ hereya workspace uninstall hereya/aws-cognito
|
|
620
661
|
```
|
|
621
662
|
|
|
622
|
-
_See code: [src/commands/workspace/uninstall/index.ts](https://github.com/hereya/hereya-cli/blob/v0.
|
|
663
|
+
_See code: [src/commands/workspace/uninstall/index.ts](https://github.com/hereya/hereya-cli/blob/v0.35.0/src/commands/workspace/uninstall/index.ts)_
|
|
623
664
|
<!-- commandsstop -->
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Config } from '../../lib/config/common.js';
|
|
2
|
+
import { AddPackageToWorkspaceInput, AddPackageToWorkspaceOutput, Backend, CreateWorkspaceInput, CreateWorkspaceOutput, DeleteWorkspaceInput, DeleteWorkspaceOutput, GetProvisioningIdInput, GetProvisioningIdOutput, GetStateInput, GetStateOutput, GetWorkspaceEnvInput, GetWorkspaceEnvOutput, GetWorkspaceOutput, InitProjectInput, InitProjectOutput, RemovePackageFromWorkspaceInput, RemovePackageFromWorkspaceOutput, SetEnvVarInput, SetEnvVarOutput, UnsetEnvVarInput, UnsetEnvVarOutput } from '../common.js';
|
|
3
|
+
interface CloudBackendConfig {
|
|
4
|
+
accessToken: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
refreshToken: string;
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class CloudBackend implements Backend {
|
|
10
|
+
private readonly config;
|
|
11
|
+
constructor(config: CloudBackendConfig);
|
|
12
|
+
addPackageToWorkspace(input: AddPackageToWorkspaceInput): Promise<AddPackageToWorkspaceOutput>;
|
|
13
|
+
createWorkspace(input: CreateWorkspaceInput): Promise<CreateWorkspaceOutput>;
|
|
14
|
+
deleteWorkspace(input: DeleteWorkspaceInput): Promise<DeleteWorkspaceOutput>;
|
|
15
|
+
getProvisioningId(input: GetProvisioningIdInput): Promise<GetProvisioningIdOutput>;
|
|
16
|
+
getState(input: GetStateInput): Promise<GetStateOutput>;
|
|
17
|
+
getWorkspace(name: string): Promise<GetWorkspaceOutput>;
|
|
18
|
+
getWorkspaceEnv(input: GetWorkspaceEnvInput): Promise<GetWorkspaceEnvOutput>;
|
|
19
|
+
init(input: InitProjectInput): Promise<InitProjectOutput>;
|
|
20
|
+
listWorkspaces(): Promise<string[]>;
|
|
21
|
+
removePackageFromWorkspace(input: RemovePackageFromWorkspaceInput): Promise<RemovePackageFromWorkspaceOutput>;
|
|
22
|
+
saveState(config: Config, workspace?: string): Promise<void>;
|
|
23
|
+
setEnvVar(input: SetEnvVarInput): Promise<SetEnvVarOutput>;
|
|
24
|
+
unsetEnvVar(input: UnsetEnvVarInput): Promise<UnsetEnvVarOutput>;
|
|
25
|
+
private convertWorkspace;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
export class CloudBackend {
|
|
2
|
+
config;
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
}
|
|
6
|
+
async addPackageToWorkspace(input) {
|
|
7
|
+
const formData = new FormData();
|
|
8
|
+
formData.append('package', input.package);
|
|
9
|
+
formData.append('infra', input.infra);
|
|
10
|
+
formData.append('env', JSON.stringify(input.env));
|
|
11
|
+
if (input.parameters) {
|
|
12
|
+
formData.append('parameters', JSON.stringify(input.parameters));
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/packages`, {
|
|
15
|
+
body: formData,
|
|
16
|
+
headers: {
|
|
17
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
18
|
+
},
|
|
19
|
+
method: 'POST',
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
return {
|
|
23
|
+
reason: JSON.stringify(await response.json()),
|
|
24
|
+
success: false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const result = await response.json();
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
workspace: this.convertWorkspace(result.workspace),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async createWorkspace(input) {
|
|
34
|
+
const formData = new FormData();
|
|
35
|
+
formData.append('name', input.name);
|
|
36
|
+
if (input.mirrorOf) {
|
|
37
|
+
formData.append('mirrorOf', input.mirrorOf);
|
|
38
|
+
}
|
|
39
|
+
const response = await fetch(`${this.config.url}/api/workspaces`, {
|
|
40
|
+
body: formData,
|
|
41
|
+
headers: {
|
|
42
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
43
|
+
},
|
|
44
|
+
method: 'POST',
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
return {
|
|
48
|
+
reason: JSON.stringify(await response.json()),
|
|
49
|
+
success: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const result = await response.json();
|
|
53
|
+
return {
|
|
54
|
+
isNew: true,
|
|
55
|
+
success: true,
|
|
56
|
+
workspace: {
|
|
57
|
+
id: result.workspace.id,
|
|
58
|
+
name: result.workspace.name,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async deleteWorkspace(input) {
|
|
63
|
+
const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.name)}`, {
|
|
64
|
+
headers: {
|
|
65
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
66
|
+
},
|
|
67
|
+
method: 'DELETE',
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
return {
|
|
71
|
+
reason: JSON.stringify(await response.json()),
|
|
72
|
+
success: false,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async getProvisioningId(input) {
|
|
80
|
+
const formData = new FormData();
|
|
81
|
+
formData.append('packageCanonicalName', input.packageCanonicalName);
|
|
82
|
+
formData.append('logicalId', input.logicalId);
|
|
83
|
+
if (input.project) {
|
|
84
|
+
formData.append('project', input.project);
|
|
85
|
+
}
|
|
86
|
+
if (input.workspace) {
|
|
87
|
+
formData.append('workspace', input.workspace);
|
|
88
|
+
}
|
|
89
|
+
const response = await fetch(`${this.config.url}/api/provisioning-id`, {
|
|
90
|
+
body: formData,
|
|
91
|
+
headers: {
|
|
92
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
93
|
+
},
|
|
94
|
+
method: 'POST',
|
|
95
|
+
});
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
return {
|
|
98
|
+
reason: JSON.stringify(await response.json()),
|
|
99
|
+
success: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const result = await response.json();
|
|
103
|
+
return {
|
|
104
|
+
id: result.provisioningId.id,
|
|
105
|
+
success: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async getState(input) {
|
|
109
|
+
const url = new URL(`${this.config.url}/api/projects/${encodeURIComponent(input.project)}/state`);
|
|
110
|
+
url.searchParams.append('workspace', input.workspace);
|
|
111
|
+
const response = await fetch(url, {
|
|
112
|
+
headers: {
|
|
113
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
114
|
+
},
|
|
115
|
+
method: 'GET',
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
return {
|
|
119
|
+
found: false,
|
|
120
|
+
reason: JSON.stringify(await response.json()),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const result = await response.json();
|
|
124
|
+
if (!result.success) {
|
|
125
|
+
return {
|
|
126
|
+
found: false,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const deploy = {};
|
|
130
|
+
for (const item of result.config.deploy) {
|
|
131
|
+
deploy[item.name] = { version: item.version };
|
|
132
|
+
}
|
|
133
|
+
const packages = {};
|
|
134
|
+
for (const item of result.config.packages) {
|
|
135
|
+
packages[item.name] = { version: item.version };
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
config: {
|
|
139
|
+
deploy,
|
|
140
|
+
packages,
|
|
141
|
+
project: input.project,
|
|
142
|
+
workspace: input.workspace,
|
|
143
|
+
},
|
|
144
|
+
found: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async getWorkspace(name) {
|
|
148
|
+
const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(name)}`, {
|
|
149
|
+
headers: {
|
|
150
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
151
|
+
},
|
|
152
|
+
method: 'GET',
|
|
153
|
+
});
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
const error = await response.json();
|
|
156
|
+
return {
|
|
157
|
+
error: JSON.stringify(error),
|
|
158
|
+
found: true,
|
|
159
|
+
hasError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const result = await response.json();
|
|
163
|
+
return {
|
|
164
|
+
found: true,
|
|
165
|
+
hasError: false,
|
|
166
|
+
workspace: this.convertWorkspace(result.workspace),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
async getWorkspaceEnv(input) {
|
|
170
|
+
const workspace$ = await this.getWorkspace(input.workspace);
|
|
171
|
+
if (!workspace$.found) {
|
|
172
|
+
return {
|
|
173
|
+
reason: `Workspace ${input.workspace} not found`,
|
|
174
|
+
success: false,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (workspace$.hasError) {
|
|
178
|
+
return {
|
|
179
|
+
reason: workspace$.error,
|
|
180
|
+
success: false,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
env: workspace$.workspace.env ?? {},
|
|
185
|
+
success: true,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async init(input) {
|
|
189
|
+
const formData = new FormData();
|
|
190
|
+
formData.append('name', input.project);
|
|
191
|
+
formData.append('workspace', input.workspace);
|
|
192
|
+
const response = await fetch(`${this.config.url}/api/projects`, {
|
|
193
|
+
body: formData,
|
|
194
|
+
headers: {
|
|
195
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
196
|
+
},
|
|
197
|
+
method: 'POST',
|
|
198
|
+
});
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
throw new Error(JSON.stringify(await response.json()));
|
|
201
|
+
}
|
|
202
|
+
const result = await response.json();
|
|
203
|
+
return {
|
|
204
|
+
project: {
|
|
205
|
+
id: result.project.id,
|
|
206
|
+
name: result.project.name,
|
|
207
|
+
},
|
|
208
|
+
workspace: {
|
|
209
|
+
id: result.project.defaultWorkspace.id,
|
|
210
|
+
name: result.project.defaultWorkspace.name,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
async listWorkspaces() {
|
|
215
|
+
const response = await fetch(`${this.config.url}/api/workspaces`, {
|
|
216
|
+
headers: {
|
|
217
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
218
|
+
},
|
|
219
|
+
method: 'GET',
|
|
220
|
+
});
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
throw new Error(JSON.stringify(await response.json()));
|
|
223
|
+
}
|
|
224
|
+
const result = await response.json();
|
|
225
|
+
return result.workspaces.map((workspace) => workspace.name);
|
|
226
|
+
}
|
|
227
|
+
async removePackageFromWorkspace(input) {
|
|
228
|
+
const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/packages/${encodeURIComponent(input.package)}`, {
|
|
229
|
+
headers: {
|
|
230
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
231
|
+
},
|
|
232
|
+
method: 'DELETE',
|
|
233
|
+
});
|
|
234
|
+
if (!response.ok) {
|
|
235
|
+
return {
|
|
236
|
+
reason: JSON.stringify(await response.json()),
|
|
237
|
+
success: false,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const result = await response.json();
|
|
241
|
+
return {
|
|
242
|
+
success: true,
|
|
243
|
+
workspace: this.convertWorkspace(result.workspace),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async saveState(config, workspace) {
|
|
247
|
+
const formData = new FormData();
|
|
248
|
+
if (workspace) {
|
|
249
|
+
formData.append('workspace', workspace);
|
|
250
|
+
}
|
|
251
|
+
if (config.deploy) {
|
|
252
|
+
const deployArray = Object.entries(config.deploy).map(([name, { version }]) => ({ name, version })).sort((a, b) => a.name.localeCompare(b.name));
|
|
253
|
+
formData.append('deploy', JSON.stringify(deployArray));
|
|
254
|
+
}
|
|
255
|
+
if (config.packages) {
|
|
256
|
+
const packagesArray = Object.entries(config.packages).map(([name, { version }]) => ({ name, version })).sort((a, b) => a.name.localeCompare(b.name));
|
|
257
|
+
formData.append('packages', JSON.stringify(packagesArray));
|
|
258
|
+
}
|
|
259
|
+
const response = await fetch(`${this.config.url}/api/projects/${encodeURIComponent(config.project)}/state`, {
|
|
260
|
+
body: formData,
|
|
261
|
+
headers: {
|
|
262
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
263
|
+
},
|
|
264
|
+
method: 'POST',
|
|
265
|
+
});
|
|
266
|
+
if (!response.ok) {
|
|
267
|
+
throw new Error(JSON.stringify(await response.json()));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async setEnvVar(input) {
|
|
271
|
+
const formData = new FormData();
|
|
272
|
+
formData.append('key', input.name);
|
|
273
|
+
formData.append('value', input.value);
|
|
274
|
+
formData.append('infrastructure', input.infrastructure);
|
|
275
|
+
const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/env`, {
|
|
276
|
+
body: formData,
|
|
277
|
+
headers: {
|
|
278
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
279
|
+
},
|
|
280
|
+
method: 'POST',
|
|
281
|
+
});
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
return {
|
|
284
|
+
reason: JSON.stringify(await response.json()),
|
|
285
|
+
success: false,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
success: true,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
async unsetEnvVar(input) {
|
|
293
|
+
const response = await fetch(`${this.config.url}/api/workspaces/${encodeURIComponent(input.workspace)}/env/${encodeURIComponent(input.name)}`, {
|
|
294
|
+
headers: {
|
|
295
|
+
'Authorization': `Bearer ${this.config.accessToken}`,
|
|
296
|
+
},
|
|
297
|
+
method: 'DELETE',
|
|
298
|
+
});
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
return {
|
|
301
|
+
reason: JSON.stringify(await response.json()),
|
|
302
|
+
success: false,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
success: true,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
convertWorkspace(workspace) {
|
|
310
|
+
const env = {};
|
|
311
|
+
for (const pkg of workspace.packages) {
|
|
312
|
+
for (const e of pkg.env) {
|
|
313
|
+
env[e.key] = `${e.infrastructure}:${e.value}`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
for (const e of workspace.env) {
|
|
317
|
+
env[e.key] = `${e.infrastructure}:${e.value}`;
|
|
318
|
+
}
|
|
319
|
+
const packages = {};
|
|
320
|
+
for (const pkg of workspace.packages) {
|
|
321
|
+
packages[pkg.name] = {
|
|
322
|
+
parameters: pkg.parameters,
|
|
323
|
+
version: pkg.version,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
env,
|
|
328
|
+
id: workspace.id,
|
|
329
|
+
mirrorOf: workspace.mirrorOf?.name,
|
|
330
|
+
name: workspace.name,
|
|
331
|
+
packages,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function loginToCloudBackend(url: string): Promise<{
|
|
2
|
+
accessToken: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
success: true;
|
|
6
|
+
} | {
|
|
7
|
+
error: string;
|
|
8
|
+
success: false;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function registerDevice(url: string): Promise<{
|
|
11
|
+
clientId: string;
|
|
12
|
+
success: true;
|
|
13
|
+
} | {
|
|
14
|
+
error: string;
|
|
15
|
+
success: false;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/* eslint-disable n/no-unsupported-features/node-builtins */
|
|
2
|
+
import machineId from 'node-machine-id';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import http from 'node:http';
|
|
5
|
+
import net from 'node:net';
|
|
6
|
+
import { browserUtils } from './utils.js';
|
|
7
|
+
export async function loginToCloudBackend(url) {
|
|
8
|
+
const registerResult = await registerDevice(url);
|
|
9
|
+
if (!registerResult.success) {
|
|
10
|
+
return registerResult;
|
|
11
|
+
}
|
|
12
|
+
const { clientId } = registerResult;
|
|
13
|
+
const codeVerifier = crypto.randomBytes(32).toString('hex');
|
|
14
|
+
const codeChallengeRaw = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
|
|
15
|
+
const codeChallenge = Buffer.from(codeChallengeRaw).toString('base64');
|
|
16
|
+
const availablePort = await getAvailablePort(3000);
|
|
17
|
+
const myRedirectUri = `http://localhost:${availablePort}/auth/cli/callback`;
|
|
18
|
+
const redirectUrl = `${url}/auth/cli/login?client_id=${clientId}&code_challenge=${codeChallenge}&redirect_uri=${myRedirectUri}`;
|
|
19
|
+
console.log('Opening browser to', redirectUrl);
|
|
20
|
+
browserUtils.open(redirectUrl);
|
|
21
|
+
const codeResult = await waitForAuthorizationCode(availablePort);
|
|
22
|
+
if (!codeResult.success) {
|
|
23
|
+
return {
|
|
24
|
+
error: codeResult.error,
|
|
25
|
+
success: false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const { code } = codeResult;
|
|
29
|
+
const formData = new FormData();
|
|
30
|
+
formData.append('clientId', clientId);
|
|
31
|
+
formData.append('code', code);
|
|
32
|
+
formData.append('codeVerifier', codeVerifier);
|
|
33
|
+
const tokenResult = await fetch(`${url}/auth/cli/code`, {
|
|
34
|
+
body: formData,
|
|
35
|
+
method: 'POST',
|
|
36
|
+
});
|
|
37
|
+
if (!tokenResult.ok) {
|
|
38
|
+
return {
|
|
39
|
+
error: `Failed to get token: ${JSON.stringify(await tokenResult.json())}`,
|
|
40
|
+
success: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const token = (await tokenResult.json());
|
|
44
|
+
return {
|
|
45
|
+
accessToken: token.data.accessToken,
|
|
46
|
+
clientId,
|
|
47
|
+
refreshToken: token.data.refreshToken,
|
|
48
|
+
success: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export async function registerDevice(url) {
|
|
52
|
+
const registerUrl = `${url}/auth/cli/register`;
|
|
53
|
+
const deviceId = await machineId.machineId();
|
|
54
|
+
const formData = new FormData();
|
|
55
|
+
formData.append('deviceId', deviceId);
|
|
56
|
+
const response = await fetch(registerUrl, {
|
|
57
|
+
body: formData,
|
|
58
|
+
method: 'POST',
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
return {
|
|
62
|
+
error: `Failed to register device: ${JSON.stringify(await response.json())}`,
|
|
63
|
+
success: false,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const result = (await response.json());
|
|
67
|
+
return {
|
|
68
|
+
clientId: result.data.clientId,
|
|
69
|
+
success: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function getAvailablePort(startPort) {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
const server = net.createServer();
|
|
75
|
+
server.unref();
|
|
76
|
+
server.on('error', () => {
|
|
77
|
+
server.listen(++startPort);
|
|
78
|
+
});
|
|
79
|
+
server.listen(startPort, () => {
|
|
80
|
+
server.close(() => resolve(startPort));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function waitForAuthorizationCode(port) {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
const server = http.createServer((req, res) => {
|
|
87
|
+
const searchParams = new URLSearchParams(req.url.split('?')[1]);
|
|
88
|
+
const code = searchParams.get('code');
|
|
89
|
+
if (code) {
|
|
90
|
+
resolve({ code, success: true });
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
resolve({ error: 'No authorization code received', success: false });
|
|
94
|
+
}
|
|
95
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
96
|
+
res.end(`
|
|
97
|
+
<!DOCTYPE html>
|
|
98
|
+
<html>
|
|
99
|
+
<head>
|
|
100
|
+
<style>
|
|
101
|
+
body {
|
|
102
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
103
|
+
display: flex;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
align-items: center;
|
|
106
|
+
height: 100vh;
|
|
107
|
+
margin: 0;
|
|
108
|
+
background: #f5f5f5;
|
|
109
|
+
}
|
|
110
|
+
.card {
|
|
111
|
+
background: white;
|
|
112
|
+
padding: 2rem;
|
|
113
|
+
border-radius: 8px;
|
|
114
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
115
|
+
text-align: center;
|
|
116
|
+
}
|
|
117
|
+
.success-icon {
|
|
118
|
+
color: #10B981;
|
|
119
|
+
font-size: 3rem;
|
|
120
|
+
margin-bottom: 1rem;
|
|
121
|
+
}
|
|
122
|
+
.message {
|
|
123
|
+
color: #666;
|
|
124
|
+
font-size: 0.9rem;
|
|
125
|
+
margin-top: 1rem;
|
|
126
|
+
}
|
|
127
|
+
</style>
|
|
128
|
+
</head>
|
|
129
|
+
<body>
|
|
130
|
+
<div class="card">
|
|
131
|
+
<div class="success-icon">✓</div>
|
|
132
|
+
<h2>Authorization Successful!</h2>
|
|
133
|
+
<p class="message">You can close this tab now (Ctrl+W or Cmd+W)</p>
|
|
134
|
+
</div>
|
|
135
|
+
</body>
|
|
136
|
+
</html>
|
|
137
|
+
`);
|
|
138
|
+
server.closeAllConnections();
|
|
139
|
+
server.close();
|
|
140
|
+
});
|
|
141
|
+
server.listen(port, () => {
|
|
142
|
+
console.log(`Waiting for authorization code on port ${port}...`);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* eslint-disable n/no-unsupported-features/node-builtins */
|
|
2
|
+
export async function logoutFromCloudBackend({ secret, url }) {
|
|
3
|
+
if (!secret) {
|
|
4
|
+
return { didDelete: false, success: true };
|
|
5
|
+
}
|
|
6
|
+
return fetch(`${url}/auth/cli/logout`, {
|
|
7
|
+
headers: {
|
|
8
|
+
Authorization: `Bearer ${secret.refreshToken}`,
|
|
9
|
+
},
|
|
10
|
+
method: 'POST',
|
|
11
|
+
});
|
|
12
|
+
}
|
package/dist/backend/common.d.ts
CHANGED
|
@@ -133,6 +133,7 @@ export type GetStateOutput = {
|
|
|
133
133
|
found: true;
|
|
134
134
|
} | {
|
|
135
135
|
found: false;
|
|
136
|
+
reason?: string;
|
|
136
137
|
};
|
|
137
138
|
export type DeleteWorkspaceInput = {
|
|
138
139
|
name: string;
|
|
@@ -158,6 +159,7 @@ export type GetProvisioningIdOutput = {
|
|
|
158
159
|
success: false;
|
|
159
160
|
};
|
|
160
161
|
export type SetEnvVarInput = {
|
|
162
|
+
infrastructure: InfrastructureType;
|
|
161
163
|
name: string;
|
|
162
164
|
value: string;
|
|
163
165
|
workspace: string;
|
|
@@ -169,6 +171,7 @@ export type SetEnvVarOutput = {
|
|
|
169
171
|
success: true;
|
|
170
172
|
};
|
|
171
173
|
export type UnsetEnvVarInput = {
|
|
174
|
+
infrastructure: InfrastructureType;
|
|
172
175
|
name: string;
|
|
173
176
|
workspace: string;
|
|
174
177
|
};
|
package/dist/backend/config.d.ts
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
|
-
import { BackendType } from
|
|
1
|
+
import { BackendType } from './index.js';
|
|
2
2
|
export declare function getCurrentBackendType(): Promise<BackendType>;
|
|
3
3
|
export declare function setBackendType(type: BackendType): Promise<void>;
|
|
4
|
-
export declare function loadBackendConfig(): Promise<
|
|
5
|
-
|
|
4
|
+
export declare function loadBackendConfig(): Promise<BackendConfig>;
|
|
5
|
+
export declare function saveCloudCredentials(credentials: {
|
|
6
|
+
accessToken: string;
|
|
7
|
+
clientId: string;
|
|
8
|
+
refreshToken: string;
|
|
9
|
+
url: string;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
export declare function deleteCloudCredentials(): Promise<{
|
|
12
|
+
didDelete: boolean;
|
|
13
|
+
originalConfig: {
|
|
14
|
+
cloud?: {
|
|
15
|
+
clientId: string;
|
|
16
|
+
url: string;
|
|
17
|
+
};
|
|
18
|
+
current: BackendType;
|
|
19
|
+
};
|
|
20
|
+
secret: {
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken: string;
|
|
23
|
+
} | null;
|
|
24
|
+
success: boolean;
|
|
6
25
|
}>;
|
|
26
|
+
export declare function getCloudCredentials(clientId: string): Promise<{
|
|
27
|
+
accessToken: string;
|
|
28
|
+
refreshToken: string;
|
|
29
|
+
} | null>;
|
|
7
30
|
export declare function getBackendConfigPath(): string;
|
|
8
31
|
export type BackendConfig = {
|
|
32
|
+
cloud?: {
|
|
33
|
+
clientId: string;
|
|
34
|
+
url: string;
|
|
35
|
+
};
|
|
9
36
|
current: BackendType;
|
|
10
37
|
};
|
package/dist/backend/config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { load, save } from
|
|
4
|
-
import { BackendType } from
|
|
3
|
+
import { load, save } from '../lib/yaml-utils.js';
|
|
4
|
+
import { BackendType } from './index.js';
|
|
5
|
+
import { secretManager } from './secrets.js';
|
|
5
6
|
export async function getCurrentBackendType() {
|
|
6
7
|
const config = await loadBackendConfig();
|
|
7
8
|
return config.current;
|
|
@@ -18,6 +19,39 @@ export async function loadBackendConfig() {
|
|
|
18
19
|
}
|
|
19
20
|
return data;
|
|
20
21
|
}
|
|
22
|
+
export async function saveCloudCredentials(credentials) {
|
|
23
|
+
await secretManager.saveSecret(credentials.clientId, {
|
|
24
|
+
accessToken: credentials.accessToken,
|
|
25
|
+
refreshToken: credentials.refreshToken,
|
|
26
|
+
});
|
|
27
|
+
const config = await loadBackendConfig();
|
|
28
|
+
config.cloud = {
|
|
29
|
+
clientId: credentials.clientId,
|
|
30
|
+
url: credentials.url,
|
|
31
|
+
};
|
|
32
|
+
config.current = BackendType.Cloud;
|
|
33
|
+
await save(config, getBackendConfigPath());
|
|
34
|
+
}
|
|
35
|
+
export async function deleteCloudCredentials() {
|
|
36
|
+
const config = await loadBackendConfig();
|
|
37
|
+
const originalConfig = { ...config };
|
|
38
|
+
if (!config.cloud) {
|
|
39
|
+
return { didDelete: false, originalConfig, secret: null, success: true };
|
|
40
|
+
}
|
|
41
|
+
const secret = await secretManager.getSecret(config.cloud.clientId);
|
|
42
|
+
if (secret) {
|
|
43
|
+
await secretManager.deleteSecret(config.cloud.clientId);
|
|
44
|
+
}
|
|
45
|
+
config.cloud = undefined;
|
|
46
|
+
if (config.current === BackendType.Cloud) {
|
|
47
|
+
config.current = BackendType.Local;
|
|
48
|
+
}
|
|
49
|
+
await save(config, getBackendConfigPath());
|
|
50
|
+
return { didDelete: true, originalConfig, secret, success: true };
|
|
51
|
+
}
|
|
52
|
+
export async function getCloudCredentials(clientId) {
|
|
53
|
+
return secretManager.getSecret(clientId);
|
|
54
|
+
}
|
|
21
55
|
export function getBackendConfigPath() {
|
|
22
56
|
return path.join(os.homedir(), '.hereya', 'backend.yaml');
|
|
23
57
|
}
|
package/dist/backend/file.js
CHANGED
|
@@ -24,7 +24,7 @@ export class FileBackend {
|
|
|
24
24
|
const { workspace } = workspace$;
|
|
25
25
|
if (workspace.mirrorOf) {
|
|
26
26
|
return {
|
|
27
|
-
reason: `Cannot add package to
|
|
27
|
+
reason: `Cannot add package to mirroring workspace ${input.workspace}`,
|
|
28
28
|
success: false,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
@@ -372,7 +372,7 @@ export class FileBackend {
|
|
|
372
372
|
const { workspace } = workspace$;
|
|
373
373
|
workspace.env = {
|
|
374
374
|
...workspace.env,
|
|
375
|
-
[input.name]: input.value
|
|
375
|
+
[input.name]: `${input.infrastructure}:${input.value}`,
|
|
376
376
|
};
|
|
377
377
|
await this.saveWorkspace(workspace, input.workspace);
|
|
378
378
|
return {
|
package/dist/backend/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Backend } from './common.js';
|
|
2
|
-
export declare function getBackend(): Promise<Backend>;
|
|
2
|
+
export declare function getBackend(type?: BackendType): Promise<Backend>;
|
|
3
3
|
export declare function setBackendType(type: BackendType): void;
|
|
4
4
|
export declare enum BackendType {
|
|
5
|
+
Cloud = "cloud",
|
|
5
6
|
Local = "local",
|
|
6
7
|
S3 = "s3"
|
|
7
8
|
}
|
package/dist/backend/index.js
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
import { getAwsConfig } from '../infrastructure/aws-config.js';
|
|
2
|
-
import {
|
|
2
|
+
import { CloudBackend } from './cloud/cloud-backend.js';
|
|
3
|
+
import { getCloudCredentials, loadBackendConfig } from './config.js';
|
|
3
4
|
import { LocalFileBackend } from './local.js';
|
|
4
5
|
import { S3FileBackend } from './s3.js';
|
|
5
6
|
let backend;
|
|
6
7
|
let currentBackendType;
|
|
7
|
-
export async function getBackend() {
|
|
8
|
+
export async function getBackend(type) {
|
|
8
9
|
if (backend) {
|
|
9
10
|
return backend;
|
|
10
11
|
}
|
|
11
|
-
const
|
|
12
|
+
const backendConfig = await loadBackendConfig();
|
|
13
|
+
const backendType = type ?? currentBackendType ?? backendConfig.current;
|
|
12
14
|
switch (backendType) {
|
|
15
|
+
case BackendType.Cloud: {
|
|
16
|
+
if (!backendConfig.cloud) {
|
|
17
|
+
throw new Error('Cloud credentials not found. Please run `hereya login` first.');
|
|
18
|
+
}
|
|
19
|
+
const credentials = await getCloudCredentials(backendConfig.cloud.clientId);
|
|
20
|
+
if (!credentials) {
|
|
21
|
+
throw new Error('Cloud credentials not found. Please run `hereya login` first.');
|
|
22
|
+
}
|
|
23
|
+
backend = new CloudBackend({
|
|
24
|
+
accessToken: credentials.accessToken,
|
|
25
|
+
clientId: backendConfig.cloud.clientId,
|
|
26
|
+
refreshToken: credentials.refreshToken,
|
|
27
|
+
url: backendConfig.cloud.url,
|
|
28
|
+
});
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
13
31
|
case BackendType.Local: {
|
|
14
32
|
backend = new LocalFileBackend();
|
|
15
33
|
break;
|
|
@@ -33,6 +51,7 @@ export function setBackendType(type) {
|
|
|
33
51
|
}
|
|
34
52
|
export var BackendType;
|
|
35
53
|
(function (BackendType) {
|
|
54
|
+
BackendType["Cloud"] = "cloud";
|
|
36
55
|
BackendType["Local"] = "local";
|
|
37
56
|
BackendType["S3"] = "s3";
|
|
38
57
|
})(BackendType || (BackendType = {}));
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const SECRETS_DIR = path.join(os.homedir(), '.hereya', 'secrets');
|
|
5
|
+
async function ensureSecretsDir() {
|
|
6
|
+
try {
|
|
7
|
+
await fs.mkdir(SECRETS_DIR, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
// Ignore if directory already exists
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function getSecretPath(name) {
|
|
14
|
+
await ensureSecretsDir();
|
|
15
|
+
return path.join(SECRETS_DIR, `${name}.json`);
|
|
16
|
+
}
|
|
17
|
+
export const secretManager = {
|
|
18
|
+
async deleteSecret(name) {
|
|
19
|
+
try {
|
|
20
|
+
const keytar = await import('keytar').then((m) => m.default);
|
|
21
|
+
await keytar.deletePassword('hereya', name);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Fallback to file-based storage
|
|
25
|
+
const secretPath = await getSecretPath(name);
|
|
26
|
+
try {
|
|
27
|
+
await fs.unlink(secretPath);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Ignore if file doesn't exist
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
async getSecret(name) {
|
|
35
|
+
try {
|
|
36
|
+
const keytar = await import('keytar').then((m) => m.default);
|
|
37
|
+
const value = await keytar.getPassword('hereya', name);
|
|
38
|
+
if (!value) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return JSON.parse(value);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Fallback to file-based storage
|
|
45
|
+
const secretPath = await getSecretPath(name);
|
|
46
|
+
try {
|
|
47
|
+
const value = await fs.readFile(secretPath, 'utf8');
|
|
48
|
+
return JSON.parse(value);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async saveSecret(name, value) {
|
|
56
|
+
try {
|
|
57
|
+
const keytar = await import('keytar').then((m) => m.default);
|
|
58
|
+
await keytar.setPassword('hereya', name, JSON.stringify(value));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Fallback to file-based storage
|
|
62
|
+
const secretPath = await getSecretPath(name);
|
|
63
|
+
await fs.writeFile(secretPath, JSON.stringify(value), 'utf8');
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -36,7 +36,7 @@ export default class Init extends Command {
|
|
|
36
36
|
workspace: flags.workspace,
|
|
37
37
|
});
|
|
38
38
|
const content = {
|
|
39
|
-
project: initProjectOutput.project.
|
|
39
|
+
project: initProjectOutput.project.name,
|
|
40
40
|
workspace: initProjectOutput.workspace.name,
|
|
41
41
|
};
|
|
42
42
|
await configManager.saveConfig({ config: content, projectRootDir });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Login extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
url: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { loginToCloudBackend } from '../../backend/cloud/login.js';
|
|
3
|
+
import { saveCloudCredentials } from '../../backend/config.js';
|
|
4
|
+
export default class Login extends Command {
|
|
5
|
+
static args = {
|
|
6
|
+
url: Args.string({ description: 'URL of the Hereya Cloud backend', required: true }),
|
|
7
|
+
};
|
|
8
|
+
static description = 'Login to the Hereya Cloud backend';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %> https://cloud.hereya.dev',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> http://localhost:5173',
|
|
12
|
+
];
|
|
13
|
+
async run() {
|
|
14
|
+
const { args } = await this.parse(Login);
|
|
15
|
+
const result = await loginToCloudBackend(args.url);
|
|
16
|
+
if (!result.success) {
|
|
17
|
+
this.error(result.error);
|
|
18
|
+
}
|
|
19
|
+
await saveCloudCredentials({
|
|
20
|
+
accessToken: result.accessToken,
|
|
21
|
+
clientId: result.clientId,
|
|
22
|
+
refreshToken: result.refreshToken,
|
|
23
|
+
url: args.url,
|
|
24
|
+
});
|
|
25
|
+
this.log(`Logged in to ${args.url}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { logoutFromCloudBackend } from '../../backend/cloud/logout.js';
|
|
3
|
+
import { deleteCloudCredentials } from '../../backend/config.js';
|
|
4
|
+
export default class Logout extends Command {
|
|
5
|
+
static description = 'Logout from Hereya Cloud';
|
|
6
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
7
|
+
async run() {
|
|
8
|
+
await this.parse(Logout);
|
|
9
|
+
const result = await deleteCloudCredentials();
|
|
10
|
+
if (result.originalConfig.cloud) {
|
|
11
|
+
await logoutFromCloudBackend({
|
|
12
|
+
secret: result.secret,
|
|
13
|
+
url: result.originalConfig.cloud.url,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (result.didDelete) {
|
|
17
|
+
this.log('Logged out from Hereya Cloud');
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
this.log('Not logged in to Hereya Cloud');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/executor/local.js
CHANGED
|
@@ -76,8 +76,9 @@ export class LocalExecutor {
|
|
|
76
76
|
return { reason: setEnvVarOutput.reason, success: false };
|
|
77
77
|
}
|
|
78
78
|
return backend.setEnvVar({
|
|
79
|
+
infrastructure: input.infra,
|
|
79
80
|
name: input.name,
|
|
80
|
-
value:
|
|
81
|
+
value: setEnvVarOutput.value,
|
|
81
82
|
workspace: input.workspace,
|
|
82
83
|
});
|
|
83
84
|
}
|
|
@@ -116,6 +117,7 @@ export class LocalExecutor {
|
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
119
|
return backend.unsetEnvVar({
|
|
120
|
+
infrastructure: infra,
|
|
119
121
|
name: input.name,
|
|
120
122
|
workspace: input.workspace,
|
|
121
123
|
});
|
|
@@ -7,24 +7,9 @@ import { getIac } from '../iac/index.js';
|
|
|
7
7
|
import { downloadPackage } from '../lib/package/index.js';
|
|
8
8
|
import { runShell } from '../lib/shell.js';
|
|
9
9
|
import { getAwsConfig, getAwsConfigKey } from './aws-config.js';
|
|
10
|
-
import { getPackageDownloadPath } from './common.js';
|
|
10
|
+
import { getPackageDownloadPath, } from './common.js';
|
|
11
11
|
import { destroyPackage, provisionPackage } from './index.js';
|
|
12
12
|
export class AwsInfrastructure {
|
|
13
|
-
// public static configKey = '/hereya-bootstrap/config'
|
|
14
|
-
// public static async getConfig(): Promise<{
|
|
15
|
-
// backendBucket: string
|
|
16
|
-
// terraformStateBucketName: string
|
|
17
|
-
// terraformStateBucketRegion?: string
|
|
18
|
-
// terraformStateLockTableName: string
|
|
19
|
-
// }> {
|
|
20
|
-
// const ssmClient = new SSMClient({})
|
|
21
|
-
// const ssmParameter = await ssmClient.send(
|
|
22
|
-
// new GetParameterCommand({
|
|
23
|
-
// Name: AwsInfrastructure.configKey,
|
|
24
|
-
// }),
|
|
25
|
-
// )
|
|
26
|
-
// return JSON.parse(ssmParameter.Parameter?.Value ?? '{}')
|
|
27
|
-
// }
|
|
28
13
|
async bootstrap(_) {
|
|
29
14
|
const stsClient = new STSClient({});
|
|
30
15
|
const { Account: accountId } = await stsClient.send(new GetCallerIdentityCommand({}));
|
|
@@ -59,7 +44,7 @@ export class AwsInfrastructure {
|
|
|
59
44
|
const downloadPath = await downloadPackage(input.pkgUrl, destPath);
|
|
60
45
|
const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION;
|
|
61
46
|
const infraConfig = {
|
|
62
|
-
...await getAwsConfig(),
|
|
47
|
+
...(await getAwsConfig()),
|
|
63
48
|
region,
|
|
64
49
|
};
|
|
65
50
|
if (!infraConfig.terraformStateBucketName || !infraConfig.terraformStateLockTableName) {
|
package/oclif.manifest.json
CHANGED
|
@@ -313,6 +313,61 @@
|
|
|
313
313
|
"index.js"
|
|
314
314
|
]
|
|
315
315
|
},
|
|
316
|
+
"login": {
|
|
317
|
+
"aliases": [],
|
|
318
|
+
"args": {
|
|
319
|
+
"url": {
|
|
320
|
+
"description": "URL of the Hereya Cloud backend",
|
|
321
|
+
"name": "url",
|
|
322
|
+
"required": true
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
"description": "Login to the Hereya Cloud backend",
|
|
326
|
+
"examples": [
|
|
327
|
+
"<%= config.bin %> <%= command.id %> https://cloud.hereya.dev",
|
|
328
|
+
"<%= config.bin %> <%= command.id %> http://localhost:5173"
|
|
329
|
+
],
|
|
330
|
+
"flags": {},
|
|
331
|
+
"hasDynamicHelp": false,
|
|
332
|
+
"hiddenAliases": [],
|
|
333
|
+
"id": "login",
|
|
334
|
+
"pluginAlias": "hereya-cli",
|
|
335
|
+
"pluginName": "hereya-cli",
|
|
336
|
+
"pluginType": "core",
|
|
337
|
+
"strict": true,
|
|
338
|
+
"enableJsonFlag": false,
|
|
339
|
+
"isESM": true,
|
|
340
|
+
"relativePath": [
|
|
341
|
+
"dist",
|
|
342
|
+
"commands",
|
|
343
|
+
"login",
|
|
344
|
+
"index.js"
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
"logout": {
|
|
348
|
+
"aliases": [],
|
|
349
|
+
"args": {},
|
|
350
|
+
"description": "Logout from Hereya Cloud",
|
|
351
|
+
"examples": [
|
|
352
|
+
"<%= config.bin %> <%= command.id %>"
|
|
353
|
+
],
|
|
354
|
+
"flags": {},
|
|
355
|
+
"hasDynamicHelp": false,
|
|
356
|
+
"hiddenAliases": [],
|
|
357
|
+
"id": "logout",
|
|
358
|
+
"pluginAlias": "hereya-cli",
|
|
359
|
+
"pluginName": "hereya-cli",
|
|
360
|
+
"pluginType": "core",
|
|
361
|
+
"strict": true,
|
|
362
|
+
"enableJsonFlag": false,
|
|
363
|
+
"isESM": true,
|
|
364
|
+
"relativePath": [
|
|
365
|
+
"dist",
|
|
366
|
+
"commands",
|
|
367
|
+
"logout",
|
|
368
|
+
"index.js"
|
|
369
|
+
]
|
|
370
|
+
},
|
|
316
371
|
"remove": {
|
|
317
372
|
"aliases": [],
|
|
318
373
|
"args": {
|
|
@@ -1063,5 +1118,5 @@
|
|
|
1063
1118
|
]
|
|
1064
1119
|
}
|
|
1065
1120
|
},
|
|
1066
|
-
"version": "0.
|
|
1121
|
+
"version": "0.35.0"
|
|
1067
1122
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hereya-cli",
|
|
3
3
|
"description": "Infrastructure as Package",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.35.0",
|
|
5
5
|
"author": "Hereya Developers",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hereya": "./bin/run.js"
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
"@oclif/plugin-plugins": "^5.4.31",
|
|
22
22
|
"glob": "^11.0.1",
|
|
23
23
|
"ignore": "^7.0.3",
|
|
24
|
+
"keytar": "^7.9.0",
|
|
24
25
|
"listr2": "^8.2.5",
|
|
26
|
+
"node-machine-id": "^1.1.12",
|
|
27
|
+
"open": "^10.1.1",
|
|
25
28
|
"simple-git": "^3.27.0",
|
|
26
29
|
"unzip-stream": "^0.3.4",
|
|
27
30
|
"yaml": "^2.7.0",
|
|
@@ -43,7 +46,7 @@
|
|
|
43
46
|
"eslint-plugin-chai-friendly": "^1.0.1",
|
|
44
47
|
"mocha": "^11.1.0",
|
|
45
48
|
"mock-fs": "^5.5.0",
|
|
46
|
-
"nock": "^14.0.
|
|
49
|
+
"nock": "^14.0.3",
|
|
47
50
|
"oclif": "^4.17.27",
|
|
48
51
|
"shx": "^0.3.4",
|
|
49
52
|
"sinon": "^19.0.2",
|