airfoil 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/index.js +843 -99650
  2. package/package.json +21 -22
  3. package/src/commands/cluster/index.ts +15 -42
  4. package/src/commands/cluster/namespace/create.ts +280 -73
  5. package/src/commands/cluster/namespace/delete.ts +12 -20
  6. package/src/commands/cluster/namespace/get.ts +15 -21
  7. package/src/commands/cluster/namespace/list.ts +17 -44
  8. package/src/commands/cluster/table/create.ts +279 -0
  9. package/src/commands/cluster/table/delete.ts +54 -0
  10. package/src/commands/cluster/table/get.ts +62 -0
  11. package/src/commands/cluster/table/list.ts +61 -0
  12. package/src/commands/dev.ts +73 -172
  13. package/src/commands/sql.ts +31 -62
  14. package/src/index.ts +13 -6
  15. package/src/utils/client.ts +3 -3
  16. package/src/utils/docker.ts +137 -0
  17. package/src/utils/options.ts +2 -4
  18. package/src/utils/wings.ts +72 -64
  19. package/src/commands/cluster/data-lake/create-iceberg.ts +0 -61
  20. package/src/commands/cluster/data-lake/create-parquet.ts +0 -61
  21. package/src/commands/cluster/data-lake/create.ts +0 -16
  22. package/src/commands/cluster/data-lake/delete.ts +0 -58
  23. package/src/commands/cluster/data-lake/get.ts +0 -46
  24. package/src/commands/cluster/data-lake/list.ts +0 -72
  25. package/src/commands/cluster/object-store/create-aws.ts +0 -106
  26. package/src/commands/cluster/object-store/create-azure.ts +0 -100
  27. package/src/commands/cluster/object-store/create-google.ts +0 -102
  28. package/src/commands/cluster/object-store/create-s3.ts +0 -126
  29. package/src/commands/cluster/object-store/create.ts +0 -20
  30. package/src/commands/cluster/object-store/delete.ts +0 -58
  31. package/src/commands/cluster/object-store/get.ts +0 -51
  32. package/src/commands/cluster/object-store/list.ts +0 -72
  33. package/src/commands/cluster/tenant/create.ts +0 -46
  34. package/src/commands/cluster/tenant/delete.ts +0 -62
  35. package/src/commands/cluster/tenant/get.ts +0 -44
  36. package/src/commands/cluster/tenant/list.ts +0 -60
  37. package/src/commands/cluster/topic/create.ts +0 -317
  38. package/src/commands/cluster/topic/delete.ts +0 -67
  39. package/src/commands/cluster/topic/get.ts +0 -51
  40. package/src/commands/cluster/topic/list.ts +0 -70
  41. package/src/scripts/compile-all.ts +0 -21
  42. package/src/utils/effect.ts +0 -11
@@ -1,102 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect, Option } from "effect";
5
- import { Command, Flag } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import { hostOption, portOption } from "../../../utils/options.js";
9
-
10
- const parentOption = Flag.string("parent").pipe(
11
- Flag.withDescription("Parent tenant in format: tenants/{tenant}"),
12
- );
13
-
14
- const objectStoreIdOption = Flag.string("object-store-id").pipe(
15
- Flag.withDescription("Unique identifier for the object store"),
16
- );
17
-
18
- const bucketNameOption = Flag.string("bucket-name").pipe(
19
- Flag.withDescription("Google Cloud Storage bucket name"),
20
- );
21
-
22
- const prefixOption = Flag.string("prefix").pipe(
23
- Flag.withDescription("Bucket prefix (optional)"),
24
- Flag.optional,
25
- );
26
-
27
- const serviceAccountOption = Flag.string("service-account").pipe(
28
- Flag.withDescription("GOOGLE_SERVICE_ACCOUNT"),
29
- );
30
-
31
- const serviceAccountKeyOption = Flag.string("service-account-key").pipe(
32
- Flag.withDescription("GOOGLE_SERVICE_ACCOUNT_KEY"),
33
- );
34
-
35
- export const createObjectStoreGoogleCommand = Command.make(
36
- "google",
37
- {
38
- parent: parentOption,
39
- objectStoreId: objectStoreIdOption,
40
- bucketName: bucketNameOption,
41
- prefix: prefixOption,
42
- serviceAccount: serviceAccountOption,
43
- serviceAccountKey: serviceAccountKeyOption,
44
- host: hostOption,
45
- port: portOption,
46
- },
47
- ({
48
- parent,
49
- objectStoreId,
50
- bucketName,
51
- prefix,
52
- serviceAccount,
53
- serviceAccountKey,
54
- host,
55
- port,
56
- }) =>
57
- Effect.gen(function* () {
58
- p.intro("🗄️ Create Google Cloud Storage Object Store");
59
-
60
- const layer = makeClusterMetadataLayer(host, port);
61
-
62
- const s = p.spinner();
63
- s.start("Creating Google Cloud Storage object store...");
64
-
65
- const result = yield* WingsClusterMetadata.createObjectStore({
66
- parent,
67
- objectStoreId,
68
- objectStoreConfig: {
69
- _tag: "google",
70
- google: {
71
- bucketName,
72
- prefix: Option.getOrUndefined(prefix),
73
- serviceAccount,
74
- serviceAccountKey,
75
- },
76
- },
77
- }).pipe(
78
- Effect.provide(layer),
79
- Effect.tapError(() =>
80
- Effect.sync(() => s.stop("Failed to create Google object store")),
81
- ),
82
- );
83
-
84
- s.stop("Google Cloud Storage object store created successfully");
85
-
86
- yield* Effect.sync(() => {
87
- printTable([
88
- {
89
- name: result.name,
90
- type: "Google Cloud",
91
- bucket: bucketName,
92
- service_account: serviceAccount,
93
- },
94
- ]);
95
- p.outro("✓ Done");
96
- });
97
- }).pipe(
98
- Effect.catch(handleCliError("Failed to create Google object store")),
99
- ),
100
- ).pipe(
101
- Command.withDescription("Create a new Google Cloud Storage object store"),
102
- );
@@ -1,126 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect, Option } from "effect";
5
- import { Command, Flag } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import { hostOption, portOption } from "../../../utils/options.js";
9
-
10
- const parentOption = Flag.string("parent").pipe(
11
- Flag.withDescription("Parent tenant in format: tenants/{tenant}"),
12
- );
13
-
14
- const objectStoreIdOption = Flag.string("object-store-id").pipe(
15
- Flag.withDescription("Unique identifier for the object store"),
16
- );
17
-
18
- const bucketNameOption = Flag.string("bucket-name").pipe(
19
- Flag.withDescription("S3 bucket name"),
20
- );
21
-
22
- const prefixOption = Flag.string("prefix").pipe(
23
- Flag.withDescription("Bucket prefix (optional)"),
24
- Flag.optional,
25
- );
26
-
27
- const accessKeyIdOption = Flag.string("access-key-id").pipe(
28
- Flag.withDescription("AWS_ACCESS_KEY_ID"),
29
- );
30
-
31
- const secretAccessKeyOption = Flag.string("secret-access-key").pipe(
32
- Flag.withDescription("AWS_SECRET_ACCESS_KEY"),
33
- );
34
-
35
- const regionOption = Flag.string("region").pipe(
36
- Flag.withDescription("AWS_DEFAULT_REGION"),
37
- Flag.optional,
38
- );
39
-
40
- const endpointOption = Flag.string("endpoint").pipe(
41
- Flag.withDescription(
42
- "AWS_ENDPOINT (e.g., https://nyc3.digitaloceanspaces.com)",
43
- ),
44
- );
45
-
46
- export const createObjectStoreS3Command = Command.make(
47
- "s3",
48
- {
49
- parent: parentOption,
50
- objectStoreId: objectStoreIdOption,
51
- bucketName: bucketNameOption,
52
- prefix: prefixOption,
53
- accessKeyId: accessKeyIdOption,
54
- secretAccessKey: secretAccessKeyOption,
55
- region: regionOption,
56
- endpoint: endpointOption,
57
- host: hostOption,
58
- port: portOption,
59
- },
60
- ({
61
- parent,
62
- objectStoreId,
63
- bucketName,
64
- prefix,
65
- accessKeyId,
66
- secretAccessKey,
67
- region,
68
- endpoint,
69
- host,
70
- port,
71
- }) =>
72
- Effect.gen(function* () {
73
- p.intro("🗄️ Create S3-Compatible Object Store");
74
-
75
- const layer = makeClusterMetadataLayer(host, port);
76
-
77
- const s = p.spinner();
78
- s.start("Creating S3-compatible object store...");
79
-
80
- const result = yield* WingsClusterMetadata.createObjectStore({
81
- parent,
82
- objectStoreId,
83
- objectStoreConfig: {
84
- _tag: "s3Compatible",
85
- s3Compatible: {
86
- bucketName,
87
- prefix: Option.getOrUndefined(prefix),
88
- accessKeyId,
89
- secretAccessKey,
90
- region: Option.getOrUndefined(region),
91
- endpoint,
92
- allowHttp: false,
93
- },
94
- },
95
- }).pipe(
96
- Effect.provide(layer),
97
- Effect.tapError(() =>
98
- Effect.sync(() =>
99
- s.stop("Failed to create S3-compatible object store"),
100
- ),
101
- ),
102
- );
103
-
104
- s.stop("S3-compatible object store created successfully");
105
-
106
- yield* Effect.sync(() => {
107
- printTable([
108
- {
109
- name: result.name,
110
- type: "S3-Compatible",
111
- bucket: bucketName,
112
- endpoint,
113
- },
114
- ]);
115
- p.outro("✓ Done");
116
- });
117
- }).pipe(
118
- Effect.catch(
119
- handleCliError("Failed to create S3-compatible object store"),
120
- ),
121
- ),
122
- ).pipe(
123
- Command.withDescription(
124
- "Create a new S3-compatible object store (MinIO, DigitalOcean, etc.)",
125
- ),
126
- );
@@ -1,20 +0,0 @@
1
- import { Effect } from "effect";
2
- import { Command } from "effect/unstable/cli";
3
- import { createObjectStoreAwsCommand } from "./create-aws.js";
4
- import { createObjectStoreAzureCommand } from "./create-azure.js";
5
- import { createObjectStoreGoogleCommand } from "./create-google.js";
6
- import { createObjectStoreS3Command } from "./create-s3.js";
7
-
8
- export const createObjectStoreCommand = Command.make(
9
- "create-object-store",
10
- {},
11
- () => Effect.void,
12
- ).pipe(
13
- Command.withDescription("Create a new object store"),
14
- Command.withSubcommands([
15
- createObjectStoreAwsCommand,
16
- createObjectStoreAzureCommand,
17
- createObjectStoreGoogleCommand,
18
- createObjectStoreS3Command,
19
- ]),
20
- );
@@ -1,58 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { Effect } from "effect";
4
- import { Command, Flag } from "effect/unstable/cli";
5
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
6
- import { handleCliError } from "../../../utils/effect.js";
7
- import { forceOption, hostOption, portOption } from "../../../utils/options.js";
8
-
9
- const nameOption = Flag.string("name").pipe(
10
- Flag.withDescription(
11
- "Object store name in format: tenants/{tenant}/object-stores/{object-store}",
12
- ),
13
- );
14
-
15
- export const deleteObjectStoreCommand = Command.make(
16
- "delete-object-store",
17
- {
18
- name: nameOption,
19
- force: forceOption,
20
- host: hostOption,
21
- port: portOption,
22
- },
23
- ({ name, force, host, port }) =>
24
- Effect.gen(function* () {
25
- p.intro("🗑️ Delete Object Store");
26
-
27
- if (!force) {
28
- const confirm = yield* Effect.tryPromise({
29
- try: () =>
30
- p.confirm({
31
- message: `Are you sure you want to delete object store ${name}?`,
32
- initialValue: false,
33
- }),
34
- catch: () => new Error("Failed to confirm deletion"),
35
- });
36
-
37
- if (p.isCancel(confirm) || !confirm) {
38
- p.cancel("Deletion cancelled");
39
- process.exit(0);
40
- }
41
- }
42
-
43
- const layer = makeClusterMetadataLayer(host, port);
44
-
45
- const s = p.spinner();
46
- s.start("Deleting object store...");
47
-
48
- yield* WingsClusterMetadata.deleteObjectStore({ name }).pipe(
49
- Effect.provide(layer),
50
- Effect.tapError(() =>
51
- Effect.sync(() => s.stop("Failed to delete object store")),
52
- ),
53
- );
54
-
55
- s.stop("Object store deleted successfully");
56
- p.outro("✓ Done");
57
- }).pipe(Effect.catch(handleCliError("Failed to delete object store"))),
58
- ).pipe(Command.withDescription("Delete an object store from the cluster"));
@@ -1,51 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect } from "effect";
5
- import { Command, Flag } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import { hostOption, portOption } from "../../../utils/options.js";
9
-
10
- const nameOption = Flag.string("name").pipe(
11
- Flag.withDescription(
12
- "Object store name in format: tenants/{tenant}/object-stores/{object-store}",
13
- ),
14
- );
15
-
16
- export const getObjectStoreCommand = Command.make(
17
- "get-object-store",
18
- {
19
- name: nameOption,
20
- host: hostOption,
21
- port: portOption,
22
- },
23
- ({ name, host, port }) =>
24
- Effect.gen(function* () {
25
- const layer = makeClusterMetadataLayer(host, port);
26
-
27
- const s = p.spinner();
28
- s.start("Fetching object store...");
29
-
30
- const objectStore = yield* WingsClusterMetadata.getObjectStore({
31
- name,
32
- }).pipe(
33
- Effect.provide(layer),
34
- Effect.tapError(() =>
35
- Effect.sync(() => s.stop("Failed to get object store")),
36
- ),
37
- );
38
-
39
- s.stop("Object store retrieved");
40
-
41
- yield* Effect.sync(() => {
42
- printTable([
43
- {
44
- name: objectStore.name,
45
- type: objectStore.objectStoreConfig._tag || "-",
46
- },
47
- ]);
48
- p.outro("✓ Done");
49
- });
50
- }).pipe(Effect.catch(handleCliError("Failed to get object store"))),
51
- ).pipe(Command.withDescription("Get details of a specific object store"));
@@ -1,72 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect, Option } from "effect";
5
- import { Command, Flag } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import {
9
- hostOption,
10
- pageSizeOption,
11
- pageTokenOption,
12
- portOption,
13
- } from "../../../utils/options.js";
14
-
15
- const parentOption = Flag.string("parent").pipe(
16
- Flag.withDescription("Parent tenant in format: tenants/{tenant}"),
17
- );
18
-
19
- export const listObjectStoresCommand = Command.make(
20
- "list-object-stores",
21
- {
22
- parent: parentOption,
23
- pageSize: pageSizeOption,
24
- pageToken: pageTokenOption,
25
- host: hostOption,
26
- port: portOption,
27
- },
28
- ({ parent, pageSize, pageToken, host, port }) =>
29
- Effect.gen(function* () {
30
- const layer = makeClusterMetadataLayer(host, port);
31
-
32
- const s = p.spinner();
33
- s.start("Fetching object stores...");
34
-
35
- const response = yield* WingsClusterMetadata.listObjectStores({
36
- parent,
37
- pageSize,
38
- pageToken: Option.getOrUndefined(pageToken),
39
- }).pipe(
40
- Effect.provide(layer),
41
- Effect.tapError(() =>
42
- Effect.sync(() => s.stop("Failed to list object stores")),
43
- ),
44
- );
45
-
46
- s.stop(`Found ${response.objectStores.length} object store(s)`);
47
-
48
- yield* Effect.sync(() => {
49
- if (response.objectStores.length === 0) {
50
- p.log.warn("No object stores found");
51
- } else {
52
- printTable(
53
- response.objectStores.map(
54
- (objectStore: {
55
- name: string;
56
- objectStoreConfig: { _tag?: string | null };
57
- }) => ({
58
- name: objectStore.name,
59
- type: objectStore.objectStoreConfig._tag || "-",
60
- }),
61
- ),
62
- );
63
- }
64
-
65
- if (response.nextPageToken) {
66
- p.log.info(`Next page token: ${response.nextPageToken}`);
67
- }
68
-
69
- p.outro("✓ Done");
70
- });
71
- }).pipe(Effect.catch(handleCliError("Failed to list object stores"))),
72
- ).pipe(Command.withDescription("List all object stores belonging to a tenant"));
@@ -1,46 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect } from "effect";
5
- import { Command, Flag } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import { hostOption, portOption } from "../../../utils/options.js";
9
-
10
- const tenantIdOption = Flag.string("tenant-id").pipe(
11
- Flag.withDescription("Unique identifier for the tenant (e.g., 'acme-corp')"),
12
- );
13
-
14
- export const createTenantCommand = Command.make(
15
- "create-tenant",
16
- {
17
- tenantId: tenantIdOption,
18
- host: hostOption,
19
- port: portOption,
20
- },
21
- ({ tenantId, host, port }) =>
22
- Effect.gen(function* () {
23
- p.intro("🏢 Create Tenant");
24
-
25
- const layer = makeClusterMetadataLayer(host, port);
26
-
27
- const s = p.spinner();
28
- s.start("Creating tenant...");
29
-
30
- const tenant = yield* WingsClusterMetadata.createTenant({
31
- tenantId,
32
- }).pipe(
33
- Effect.provide(layer),
34
- Effect.tapError(() =>
35
- Effect.sync(() => s.stop("Failed to create tenant")),
36
- ),
37
- );
38
-
39
- s.stop("Tenant created successfully");
40
-
41
- yield* Effect.sync(() => {
42
- printTable([{ name: tenant.name }]);
43
- p.outro("✓ Done");
44
- });
45
- }).pipe(Effect.catch(handleCliError("Failed to create tenant"))),
46
- ).pipe(Command.withDescription("Create a new tenant in the cluster"));
@@ -1,62 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { Effect } from "effect";
4
- import { Command, Flag } from "effect/unstable/cli";
5
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
6
- import { handleCliError } from "../../../utils/effect.js";
7
- import { forceOption, hostOption, portOption } from "../../../utils/options.js";
8
-
9
- const nameOption = Flag.string("name").pipe(
10
- Flag.withDescription(
11
- "Tenant name in format: tenants/{tenant} (e.g., 'tenants/acme-corp')",
12
- ),
13
- );
14
-
15
- export const deleteTenantCommand = Command.make(
16
- "delete-tenant",
17
- {
18
- name: nameOption,
19
- force: forceOption,
20
- host: hostOption,
21
- port: portOption,
22
- },
23
- ({ name, force, host, port }) =>
24
- Effect.gen(function* () {
25
- p.intro("🗑️ Delete Tenant");
26
-
27
- if (!force) {
28
- const confirm = yield* Effect.tryPromise({
29
- try: () =>
30
- p.confirm({
31
- message: `Are you sure you want to delete tenant ${name}?`,
32
- initialValue: false,
33
- }),
34
- catch: () => new Error("Failed to confirm deletion"),
35
- });
36
-
37
- if (p.isCancel(confirm) || !confirm) {
38
- p.cancel("Deletion cancelled");
39
- process.exit(0);
40
- }
41
- }
42
-
43
- const layer = makeClusterMetadataLayer(host, port);
44
-
45
- const s = p.spinner();
46
- s.start("Deleting tenant...");
47
-
48
- yield* WingsClusterMetadata.deleteTenant({ name }).pipe(
49
- Effect.provide(layer),
50
- Effect.tapError(() =>
51
- Effect.sync(() => s.stop("Failed to delete tenant")),
52
- ),
53
- );
54
-
55
- s.stop("Tenant deleted successfully");
56
- p.outro("✓ Done");
57
- }).pipe(Effect.catch(handleCliError("Failed to delete tenant"))),
58
- ).pipe(
59
- Command.withDescription(
60
- "Delete a tenant from the cluster (fails if tenant has any namespaces)",
61
- ),
62
- );
@@ -1,44 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect } from "effect";
5
- import { Command, Flag } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import { hostOption, portOption } from "../../../utils/options.js";
9
-
10
- const nameOption = Flag.string("name").pipe(
11
- Flag.withDescription(
12
- "Tenant name in format: tenants/{tenant} (e.g., 'tenants/acme-corp')",
13
- ),
14
- );
15
-
16
- export const getTenantCommand = Command.make(
17
- "get-tenant",
18
- {
19
- name: nameOption,
20
- host: hostOption,
21
- port: portOption,
22
- },
23
- ({ name, host, port }) =>
24
- Effect.gen(function* () {
25
- const layer = makeClusterMetadataLayer(host, port);
26
-
27
- const s = p.spinner();
28
- s.start("Fetching tenant...");
29
-
30
- const tenant = yield* WingsClusterMetadata.getTenant({ name }).pipe(
31
- Effect.provide(layer),
32
- Effect.tapError(() =>
33
- Effect.sync(() => s.stop("Failed to get tenant")),
34
- ),
35
- );
36
-
37
- s.stop("Tenant retrieved");
38
-
39
- yield* Effect.sync(() => {
40
- printTable([{ name: tenant.name }]);
41
- p.outro("✓ Done");
42
- });
43
- }).pipe(Effect.catch(handleCliError("Failed to get tenant"))),
44
- ).pipe(Command.withDescription("Get details of a specific tenant"));
@@ -1,60 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import { WingsClusterMetadata } from "@useairfoil/wings";
3
- import { printTable } from "console-table-printer";
4
- import { Effect, Option } from "effect";
5
- import { Command } from "effect/unstable/cli";
6
- import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
- import { handleCliError } from "../../../utils/effect.js";
8
- import {
9
- hostOption,
10
- pageSizeOption,
11
- pageTokenOption,
12
- portOption,
13
- } from "../../../utils/options.js";
14
-
15
- export const listTenantsCommand = Command.make(
16
- "list-tenants",
17
- {
18
- pageSize: pageSizeOption,
19
- pageToken: pageTokenOption,
20
- host: hostOption,
21
- port: portOption,
22
- },
23
- ({ pageSize, pageToken, host, port }) =>
24
- Effect.gen(function* () {
25
- const layer = makeClusterMetadataLayer(host, port);
26
-
27
- const s = p.spinner();
28
- s.start("Fetching tenants...");
29
-
30
- const response = yield* WingsClusterMetadata.listTenants({
31
- pageSize,
32
- pageToken: Option.getOrUndefined(pageToken),
33
- }).pipe(
34
- Effect.provide(layer),
35
- Effect.tapError(() =>
36
- Effect.sync(() => s.stop("Failed to list tenants")),
37
- ),
38
- );
39
-
40
- s.stop(`Found ${response.tenants.length} tenant(s)`);
41
-
42
- yield* Effect.sync(() => {
43
- if (response.tenants.length === 0) {
44
- p.log.warn("No tenants found");
45
- } else {
46
- printTable(
47
- response.tenants.map((tenant: { name: string }) => ({
48
- name: tenant.name,
49
- })),
50
- );
51
- }
52
-
53
- if (response.nextPageToken) {
54
- p.log.info(`Next page token: ${response.nextPageToken}`);
55
- }
56
-
57
- p.outro("✓ Done");
58
- });
59
- }).pipe(Effect.catch(handleCliError("Failed to list tenants"))),
60
- ).pipe(Command.withDescription("List all tenants in the cluster"));