airfoil 0.1.1 → 0.3.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.
Files changed (34) hide show
  1. package/dist/index.js +72253 -73199
  2. package/package.json +6 -7
  3. package/src/commands/cluster/data-lake/create-iceberg.ts +7 -7
  4. package/src/commands/cluster/data-lake/create-parquet.ts +7 -7
  5. package/src/commands/cluster/data-lake/create.ts +1 -1
  6. package/src/commands/cluster/data-lake/delete.ts +5 -5
  7. package/src/commands/cluster/data-lake/get.ts +5 -5
  8. package/src/commands/cluster/data-lake/list.ts +5 -5
  9. package/src/commands/cluster/index.ts +1 -1
  10. package/src/commands/cluster/namespace/create.ts +17 -17
  11. package/src/commands/cluster/namespace/delete.ts +5 -5
  12. package/src/commands/cluster/namespace/get.ts +5 -5
  13. package/src/commands/cluster/namespace/list.ts +5 -5
  14. package/src/commands/cluster/object-store/create-aws.ts +19 -21
  15. package/src/commands/cluster/object-store/create-azure.ts +16 -16
  16. package/src/commands/cluster/object-store/create-google.ts +16 -16
  17. package/src/commands/cluster/object-store/create-s3.ts +22 -21
  18. package/src/commands/cluster/object-store/create.ts +1 -1
  19. package/src/commands/cluster/object-store/delete.ts +5 -5
  20. package/src/commands/cluster/object-store/get.ts +5 -5
  21. package/src/commands/cluster/object-store/list.ts +5 -5
  22. package/src/commands/cluster/tenant/create.ts +5 -7
  23. package/src/commands/cluster/tenant/delete.ts +5 -5
  24. package/src/commands/cluster/tenant/get.ts +5 -5
  25. package/src/commands/cluster/tenant/list.ts +3 -3
  26. package/src/commands/cluster/topic/create.ts +69 -58
  27. package/src/commands/cluster/topic/delete.ts +8 -8
  28. package/src/commands/cluster/topic/get.ts +5 -5
  29. package/src/commands/cluster/topic/list.ts +10 -16
  30. package/src/commands/dev.ts +23 -23
  31. package/src/commands/sql.ts +17 -17
  32. package/src/index.ts +7 -5
  33. package/src/utils/client.ts +1 -1
  34. package/src/utils/options.ts +18 -17
@@ -1,13 +1,13 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { Effect } from "effect";
4
+ import { Command, Flag } from "effect/unstable/cli";
5
5
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
6
6
  import { handleCliError } from "../../../utils/effect.js";
7
7
  import { forceOption, hostOption, portOption } from "../../../utils/options.js";
8
8
 
9
- const nameOption = Options.text("name").pipe(
10
- Options.withDescription(
9
+ const nameOption = Flag.string("name").pipe(
10
+ Flag.withDescription(
11
11
  "Object store name in format: tenants/{tenant}/object-stores/{object-store}",
12
12
  ),
13
13
  );
@@ -54,5 +54,5 @@ export const deleteObjectStoreCommand = Command.make(
54
54
 
55
55
  s.stop("Object store deleted successfully");
56
56
  p.outro("✓ Done");
57
- }).pipe(Effect.catchAll(handleCliError("Failed to delete object store"))),
57
+ }).pipe(Effect.catch(handleCliError("Failed to delete object store"))),
58
58
  ).pipe(Command.withDescription("Delete an object store from the cluster"));
@@ -1,14 +1,14 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect } from "effect";
5
+ import { Command, Flag } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import { hostOption, portOption } from "../../../utils/options.js";
9
9
 
10
- const nameOption = Options.text("name").pipe(
11
- Options.withDescription(
10
+ const nameOption = Flag.string("name").pipe(
11
+ Flag.withDescription(
12
12
  "Object store name in format: tenants/{tenant}/object-stores/{object-store}",
13
13
  ),
14
14
  );
@@ -47,5 +47,5 @@ export const getObjectStoreCommand = Command.make(
47
47
  ]);
48
48
  p.outro("✓ Done");
49
49
  });
50
- }).pipe(Effect.catchAll(handleCliError("Failed to get object store"))),
50
+ }).pipe(Effect.catch(handleCliError("Failed to get object store"))),
51
51
  ).pipe(Command.withDescription("Get details of a specific object store"));
@@ -1,8 +1,8 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect, Option } from "effect";
5
+ import { Command, Flag } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import {
@@ -12,8 +12,8 @@ import {
12
12
  portOption,
13
13
  } from "../../../utils/options.js";
14
14
 
15
- const parentOption = Options.text("parent").pipe(
16
- Options.withDescription("Parent tenant in format: tenants/{tenant}"),
15
+ const parentOption = Flag.string("parent").pipe(
16
+ Flag.withDescription("Parent tenant in format: tenants/{tenant}"),
17
17
  );
18
18
 
19
19
  export const listObjectStoresCommand = Command.make(
@@ -68,5 +68,5 @@ export const listObjectStoresCommand = Command.make(
68
68
 
69
69
  p.outro("✓ Done");
70
70
  });
71
- }).pipe(Effect.catchAll(handleCliError("Failed to list object stores"))),
71
+ }).pipe(Effect.catch(handleCliError("Failed to list object stores"))),
72
72
  ).pipe(Command.withDescription("List all object stores belonging to a tenant"));
@@ -1,16 +1,14 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect } from "effect";
5
+ import { Command, Flag } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import { hostOption, portOption } from "../../../utils/options.js";
9
9
 
10
- const tenantIdOption = Options.text("tenant-id").pipe(
11
- Options.withDescription(
12
- "Unique identifier for the tenant (e.g., 'acme-corp')",
13
- ),
10
+ const tenantIdOption = Flag.string("tenant-id").pipe(
11
+ Flag.withDescription("Unique identifier for the tenant (e.g., 'acme-corp')"),
14
12
  );
15
13
 
16
14
  export const createTenantCommand = Command.make(
@@ -44,5 +42,5 @@ export const createTenantCommand = Command.make(
44
42
  printTable([{ name: tenant.name }]);
45
43
  p.outro("✓ Done");
46
44
  });
47
- }).pipe(Effect.catchAll(handleCliError("Failed to create tenant"))),
45
+ }).pipe(Effect.catch(handleCliError("Failed to create tenant"))),
48
46
  ).pipe(Command.withDescription("Create a new tenant in the cluster"));
@@ -1,13 +1,13 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { Effect } from "effect";
4
+ import { Command, Flag } from "effect/unstable/cli";
5
5
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
6
6
  import { handleCliError } from "../../../utils/effect.js";
7
7
  import { forceOption, hostOption, portOption } from "../../../utils/options.js";
8
8
 
9
- const nameOption = Options.text("name").pipe(
10
- Options.withDescription(
9
+ const nameOption = Flag.string("name").pipe(
10
+ Flag.withDescription(
11
11
  "Tenant name in format: tenants/{tenant} (e.g., 'tenants/acme-corp')",
12
12
  ),
13
13
  );
@@ -54,7 +54,7 @@ export const deleteTenantCommand = Command.make(
54
54
 
55
55
  s.stop("Tenant deleted successfully");
56
56
  p.outro("✓ Done");
57
- }).pipe(Effect.catchAll(handleCliError("Failed to delete tenant"))),
57
+ }).pipe(Effect.catch(handleCliError("Failed to delete tenant"))),
58
58
  ).pipe(
59
59
  Command.withDescription(
60
60
  "Delete a tenant from the cluster (fails if tenant has any namespaces)",
@@ -1,14 +1,14 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect } from "effect";
5
+ import { Command, Flag } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import { hostOption, portOption } from "../../../utils/options.js";
9
9
 
10
- const nameOption = Options.text("name").pipe(
11
- Options.withDescription(
10
+ const nameOption = Flag.string("name").pipe(
11
+ Flag.withDescription(
12
12
  "Tenant name in format: tenants/{tenant} (e.g., 'tenants/acme-corp')",
13
13
  ),
14
14
  );
@@ -40,5 +40,5 @@ export const getTenantCommand = Command.make(
40
40
  printTable([{ name: tenant.name }]);
41
41
  p.outro("✓ Done");
42
42
  });
43
- }).pipe(Effect.catchAll(handleCliError("Failed to get tenant"))),
43
+ }).pipe(Effect.catch(handleCliError("Failed to get tenant"))),
44
44
  ).pipe(Command.withDescription("Get details of a specific tenant"));
@@ -1,8 +1,8 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect, Option } from "effect";
5
+ import { Command } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import {
@@ -56,5 +56,5 @@ export const listTenantsCommand = Command.make(
56
56
 
57
57
  p.outro("✓ Done");
58
58
  });
59
- }).pipe(Effect.catchAll(handleCliError("Failed to list tenants"))),
59
+ }).pipe(Effect.catch(handleCliError("Failed to list tenants"))),
60
60
  ).pipe(Command.withDescription("List all tenants in the cluster"));
@@ -1,10 +1,10 @@
1
1
  import * as fs from "node:fs";
2
- import type { FieldConfig } from "@airfoil/wings";
3
- import { WingsClusterMetadata } from "@airfoil/wings";
4
2
  import * as p from "@clack/prompts";
5
- import { Command, Options } from "@effect/cli";
3
+ import type { FieldConfig } from "@useairfoil/wings";
4
+ import { WingsClusterMetadata } from "@useairfoil/wings";
6
5
  import { printTable } from "console-table-printer";
7
6
  import { Effect, Option } from "effect";
7
+ import { Command, Flag } from "effect/unstable/cli";
8
8
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
9
9
  import { handleCliError } from "../../../utils/effect.js";
10
10
  import { hostOption, portOption } from "../../../utils/options.js";
@@ -26,14 +26,8 @@ const SUPPORTED_INLINE_TYPES = [
26
26
  "Float16",
27
27
  "Float32",
28
28
  "Float64",
29
- "LargeUtf8",
30
- "LargeBinary",
31
29
  "DateDay",
32
30
  "DateMillisecond",
33
- "TimeSecond",
34
- "TimeMillisecond",
35
- "TimeMicrosecond",
36
- "TimeNanosecond",
37
31
  "TimestampSecond",
38
32
  "TimestampMillisecond",
39
33
  "TimestampMicrosecond",
@@ -42,61 +36,63 @@ const SUPPORTED_INLINE_TYPES = [
42
36
  "DurationMillisecond",
43
37
  "DurationMicrosecond",
44
38
  "DurationNanosecond",
45
- "IntervalDayTime",
46
- "IntervalYearMonth",
47
- "IntervalMonthDayNano",
48
39
  ] as const;
49
40
 
50
41
  type SupportedInlineType = (typeof SUPPORTED_INLINE_TYPES)[number];
51
42
 
52
- const parentOption = Options.text("parent").pipe(
53
- Options.withDescription(
43
+ const parentOption = Flag.string("parent").pipe(
44
+ Flag.withDescription(
54
45
  "Parent namespace in format: tenants/{tenant}/namespaces/{namespace}",
55
46
  ),
56
47
  );
57
48
 
58
- const topicIdOption = Options.text("topic-id").pipe(
59
- Options.withDescription("Unique identifier for the topic"),
49
+ const topicIdOption = Flag.string("topic-id").pipe(
50
+ Flag.withDescription("Unique identifier for the topic"),
60
51
  );
61
52
 
62
- const descriptionOption = Options.text("description").pipe(
63
- Options.withDescription("Topic description"),
64
- Options.optional,
53
+ const descriptionOption = Flag.string("description").pipe(
54
+ Flag.withDescription("Topic description"),
55
+ Flag.optional,
65
56
  );
66
57
 
67
- const fieldsOption = Options.text("fields").pipe(
68
- Options.withDescription(
58
+ const fieldsOption = Flag.string("fields").pipe(
59
+ Flag.withDescription(
69
60
  'Field definitions in format "name:Type" or "name:Type?" for nullable',
70
61
  ),
71
- Options.repeated,
62
+ Flag.atLeast(0),
72
63
  );
73
64
 
74
- const schemaFileOption = Options.text("schema-file").pipe(
75
- Options.withDescription(
65
+ const schemaFileOption = Flag.string("schema-file").pipe(
66
+ Flag.withDescription(
76
67
  "Path to JSON file containing FieldConfig[] (for complex types)",
77
68
  ),
78
- Options.optional,
69
+ Flag.optional,
79
70
  );
80
71
 
81
- const partitionKeyOption = Options.text("partition-key").pipe(
82
- Options.withDescription("Name of the field used for partitioning"),
83
- Options.optional,
72
+ const partitionKeyOption = Flag.string("partition-key").pipe(
73
+ Flag.withDescription("Name of the field used for partitioning"),
74
+ Flag.optional,
84
75
  );
85
76
 
86
- const freshnessSecondsOption = Options.integer("freshness-seconds").pipe(
87
- Options.withDescription("How often to compact the topic (seconds)"),
88
- Options.withDefault(60),
77
+ const freshnessSecondsOption = Flag.integer("freshness-seconds").pipe(
78
+ Flag.withDescription("How often to compact the topic (seconds)"),
79
+ Flag.withDefault(60),
89
80
  );
90
81
 
91
- const ttlSecondsOption = Options.integer("ttl-seconds").pipe(
92
- Options.withDescription("How long to keep topic data (seconds)"),
93
- Options.optional,
82
+ const ttlSecondsOption = Flag.integer("ttl-seconds").pipe(
83
+ Flag.withDescription("How long to keep topic data (seconds)"),
84
+ Flag.optional,
85
+ );
86
+
87
+ const targetFileSizeBytesOption = Flag.integer("target-file-size-bytes").pipe(
88
+ Flag.withDescription("Target file size for compaction (bytes)"),
89
+ Flag.withDefault(1024 * 1024),
94
90
  );
95
91
 
96
92
  /**
97
93
  * Parse a field string in format "name:Type" or "name:Type?" (nullable)
98
94
  */
99
- function parseFieldString(fieldStr: string): FieldConfig {
95
+ function parseFieldString(fieldStr: string, index: number): FieldConfig {
100
96
  const match = fieldStr.match(/^([^:]+):([^?]+)(\?)?$/);
101
97
  if (!match) {
102
98
  throw new Error(
@@ -126,14 +122,15 @@ function parseFieldString(fieldStr: string): FieldConfig {
126
122
  name: name.trim(),
127
123
  dataType: dataType as SupportedInlineType,
128
124
  nullable,
129
- } as FieldConfig;
125
+ id: BigInt(index + 1),
126
+ };
130
127
  }
131
128
 
132
129
  /**
133
130
  * Parse multiple field strings into FieldConfig array
134
131
  */
135
- function parseFieldsFromArgs(fields: string[]): FieldConfig[] {
136
- return fields.map(parseFieldString);
132
+ function parseFieldsFromArgs(fields: ReadonlyArray<string>): FieldConfig[] {
133
+ return fields.map((field, index) => parseFieldString(field, index));
137
134
  }
138
135
 
139
136
  /**
@@ -165,7 +162,13 @@ function loadFieldsFromFile(filePath: string): FieldConfig[] {
165
162
  );
166
163
  }
167
164
 
168
- return parsed as FieldConfig[];
165
+ return (parsed as FieldConfig[]).map((field, index) => {
166
+ const fieldId = (field as { id?: bigint | number }).id;
167
+ return {
168
+ ...field,
169
+ id: typeof fieldId === "bigint" ? fieldId : BigInt(fieldId ?? index + 1),
170
+ };
171
+ });
169
172
  }
170
173
 
171
174
  export const createTopicCommand = Command.make(
@@ -179,6 +182,7 @@ export const createTopicCommand = Command.make(
179
182
  partitionKey: partitionKeyOption,
180
183
  freshnessSeconds: freshnessSecondsOption,
181
184
  ttlSeconds: ttlSecondsOption,
185
+ targetFileSizeBytes: targetFileSizeBytesOption,
182
186
  host: hostOption,
183
187
  port: portOption,
184
188
  },
@@ -191,6 +195,7 @@ export const createTopicCommand = Command.make(
191
195
  partitionKey,
192
196
  freshnessSeconds,
193
197
  ttlSeconds,
198
+ targetFileSizeBytes,
194
199
  host,
195
200
  port,
196
201
  }) =>
@@ -226,7 +231,7 @@ export const createTopicCommand = Command.make(
226
231
  }
227
232
 
228
233
  const partitionKeyName = Option.getOrUndefined(partitionKey);
229
- let partitionKeyIndex: number | undefined;
234
+ let partitionKeyId: bigint | undefined;
230
235
  if (partitionKeyName) {
231
236
  const index = topicFields.findIndex(
232
237
  (field) => field.name === partitionKeyName,
@@ -238,7 +243,15 @@ export const createTopicCommand = Command.make(
238
243
  ),
239
244
  );
240
245
  }
241
- partitionKeyIndex = index;
246
+ const partitionField = topicFields[index];
247
+ if (!partitionField) {
248
+ return yield* Effect.fail(
249
+ new Error(
250
+ `Partition key field "${partitionKeyName}" not found in fields. Available fields: ${topicFields.map((field) => field.name).join(", ")}`,
251
+ ),
252
+ );
253
+ }
254
+ partitionKeyId = partitionField.id;
242
255
  }
243
256
 
244
257
  const layer = makeClusterMetadataLayer(host, port);
@@ -251,12 +264,13 @@ export const createTopicCommand = Command.make(
251
264
  topicId,
252
265
  description: Option.getOrUndefined(description),
253
266
  fields: topicFields,
254
- partitionKey: partitionKeyIndex,
267
+ partitionKey: partitionKeyId,
255
268
  compaction: {
256
269
  freshnessSeconds: BigInt(freshnessSeconds),
257
270
  ttlSeconds: Option.getOrUndefined(
258
271
  Option.map(ttlSeconds, (ttl) => BigInt(ttl)),
259
272
  ),
273
+ targetFileSizeBytes: BigInt(targetFileSizeBytes),
260
274
  },
261
275
  }).pipe(
262
276
  Effect.provide(layer),
@@ -274,33 +288,30 @@ export const createTopicCommand = Command.make(
274
288
  description: topic.description || "-",
275
289
  partition_key:
276
290
  topic.partitionKey !== undefined
277
- ? topicFields[topic.partitionKey]?.name ||
278
- topic.partitionKey.toString()
291
+ ? topicFields.find((field) => field.id === topic.partitionKey)
292
+ ?.name || topic.partitionKey.toString()
279
293
  : "-",
280
294
  freshness_seconds: topic.compaction.freshnessSeconds.toString(),
281
295
  ttl_seconds: topic.compaction.ttlSeconds?.toString() || "-",
282
- fields_count: topic.fields.length.toString(),
296
+ target_file_size_bytes:
297
+ topic.compaction.targetFileSizeBytes.toString(),
298
+ fields_count: topic.schema.fields.length.toString(),
283
299
  },
284
300
  ]);
285
301
 
286
- if (topic.fields.length > 0) {
302
+ if (topic.schema.fields.length > 0) {
287
303
  console.log("\nFields:");
288
304
  printTable(
289
- topic.fields.map(
290
- (
291
- field: { name: string; dataType: string; nullable: boolean },
292
- index: number,
293
- ) => ({
294
- index: index.toString(),
295
- name: field.name,
296
- type: field.dataType,
297
- nullable: field.nullable ? "yes" : "no",
298
- }),
299
- ),
305
+ topic.schema.fields.map((field, index) => ({
306
+ index: index.toString(),
307
+ name: field.name,
308
+ type: field.arrowType?._tag ?? "unknown",
309
+ nullable: field.nullable ? "yes" : "no",
310
+ })),
300
311
  );
301
312
  }
302
313
 
303
314
  p.outro("✓ Done");
304
315
  });
305
- }).pipe(Effect.catchAll(handleCliError("Failed to create topic"))),
316
+ }).pipe(Effect.catch(handleCliError("Failed to create topic"))),
306
317
  ).pipe(Command.withDescription("Create a new topic belonging to a namespace"));
@@ -1,20 +1,20 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { Effect } from "effect";
4
+ import { Command, Flag } from "effect/unstable/cli";
5
5
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
6
6
  import { handleCliError } from "../../../utils/effect.js";
7
7
  import { forceOption, hostOption, portOption } from "../../../utils/options.js";
8
8
 
9
- const nameOption = Options.text("name").pipe(
10
- Options.withDescription(
9
+ const nameOption = Flag.string("name").pipe(
10
+ Flag.withDescription(
11
11
  "Topic name in format: tenants/{tenant}/namespaces/{namespace}/topics/{topic}",
12
12
  ),
13
13
  );
14
14
 
15
- const forceDeleteOption = Options.boolean("force-delete").pipe(
16
- Options.withDescription("Also delete data associated with the topic"),
17
- Options.withDefault(false),
15
+ const forceDeleteOption = Flag.boolean("force-delete").pipe(
16
+ Flag.withDescription("Also delete data associated with the topic"),
17
+ Flag.withDefault(false),
18
18
  );
19
19
 
20
20
  export const deleteTopicCommand = Command.make(
@@ -63,5 +63,5 @@ export const deleteTopicCommand = Command.make(
63
63
 
64
64
  s.stop("Topic deleted successfully");
65
65
  p.outro("✓ Done");
66
- }).pipe(Effect.catchAll(handleCliError("Failed to delete topic"))),
66
+ }).pipe(Effect.catch(handleCliError("Failed to delete topic"))),
67
67
  ).pipe(Command.withDescription("Delete a topic from the cluster"));
@@ -1,14 +1,14 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect } from "effect";
5
+ import { Command, Flag } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import { hostOption, portOption } from "../../../utils/options.js";
9
9
 
10
- const nameOption = Options.text("name").pipe(
11
- Options.withDescription(
10
+ const nameOption = Flag.string("name").pipe(
11
+ Flag.withDescription(
12
12
  "Topic name in format: tenants/{tenant}/namespaces/{namespace}/topics/{topic}",
13
13
  ),
14
14
  );
@@ -47,5 +47,5 @@ export const getTopicCommand = Command.make(
47
47
 
48
48
  p.outro("✓ Done");
49
49
  });
50
- }).pipe(Effect.catchAll(handleCliError("Failed to get topic"))),
50
+ }).pipe(Effect.catch(handleCliError("Failed to get topic"))),
51
51
  ).pipe(Command.withDescription("Get details of a specific topic"));
@@ -1,8 +1,8 @@
1
- import { WingsClusterMetadata } from "@airfoil/wings";
2
1
  import * as p from "@clack/prompts";
3
- import { Command, Options } from "@effect/cli";
2
+ import { WingsClusterMetadata } from "@useairfoil/wings";
4
3
  import { printTable } from "console-table-printer";
5
4
  import { Effect, Option } from "effect";
5
+ import { Command, Flag } from "effect/unstable/cli";
6
6
  import { makeClusterMetadataLayer } from "../../../utils/client.js";
7
7
  import { handleCliError } from "../../../utils/effect.js";
8
8
  import {
@@ -12,8 +12,8 @@ import {
12
12
  portOption,
13
13
  } from "../../../utils/options.js";
14
14
 
15
- const parentOption = Options.text("parent").pipe(
16
- Options.withDescription(
15
+ const parentOption = Flag.string("parent").pipe(
16
+ Flag.withDescription(
17
17
  "Parent namespace in format: tenants/{tenant}/namespaces/{namespace}",
18
18
  ),
19
19
  );
@@ -52,17 +52,11 @@ export const listTopicsCommand = Command.make(
52
52
  p.log.warn("No topics found");
53
53
  } else {
54
54
  printTable(
55
- response.topics.map(
56
- (topic: {
57
- name: string;
58
- description?: string | null;
59
- partitionKey?: number | null;
60
- }) => ({
61
- name: topic.name,
62
- description: topic.description || "-",
63
- partition_key: topic.partitionKey?.toString() || "-",
64
- }),
65
- ),
55
+ response.topics.map((topic) => ({
56
+ name: topic.name,
57
+ description: topic.description || "-",
58
+ partition_key: topic.partitionKey?.toString() || "-",
59
+ })),
66
60
  );
67
61
  }
68
62
 
@@ -72,5 +66,5 @@ export const listTopicsCommand = Command.make(
72
66
 
73
67
  p.outro("✓ Done");
74
68
  });
75
- }).pipe(Effect.catchAll(handleCliError("Failed to list topics"))),
69
+ }).pipe(Effect.catch(handleCliError("Failed to list topics"))),
76
70
  ).pipe(Command.withDescription("List all topics belonging to a namespace"));
@@ -1,40 +1,40 @@
1
1
  import * as p from "@clack/prompts";
2
- import { Command, Options } from "@effect/cli";
3
2
  import { Effect } from "effect";
3
+ import { Command, Flag } from "effect/unstable/cli";
4
4
  import { handleCliError } from "../utils/effect.js";
5
5
  import { downloadWings, getWingsPath, verifyChecksum } from "../utils/wings.js";
6
6
 
7
- const dockerOption = Options.boolean("docker").pipe(
8
- Options.withDescription("Run Wings using Docker (recommended)"),
9
- Options.withDefault(false),
7
+ const dockerOption = Flag.boolean("docker").pipe(
8
+ Flag.withDescription("Run Wings using Docker (recommended)"),
9
+ Flag.withDefault(false),
10
10
  );
11
11
 
12
- const versionOption = Options.text("version").pipe(
13
- Options.withDescription(
12
+ const versionOption = Flag.string("version").pipe(
13
+ Flag.withDescription(
14
14
  "Specify Wings version (e.g., v0.1.0-alpha.11 or 'latest')",
15
15
  ),
16
- Options.withDefault("latest"),
16
+ Flag.withDefault("latest"),
17
17
  );
18
18
 
19
- const tagOption = Options.text("tag").pipe(
20
- Options.withDescription("Docker image tag (only with --docker)"),
21
- Options.withDefault("latest"),
19
+ const tagOption = Flag.string("tag").pipe(
20
+ Flag.withDescription("Docker image tag (only with --docker)"),
21
+ Flag.withDefault("latest"),
22
22
  );
23
23
 
24
- const forcePullOption = Options.boolean("force-pull").pipe(
25
- Options.withDescription("Force pull Docker image even if it exists locally"),
26
- Options.withDefault(false),
24
+ const forcePullOption = Flag.boolean("force-pull").pipe(
25
+ Flag.withDescription("Force pull Docker image even if it exists locally"),
26
+ Flag.withDefault(false),
27
27
  );
28
28
 
29
- const stressOption = Options.boolean("stress").pipe(
30
- Options.withDescription("Use Wings stress binary variant"),
31
- Options.withDefault(false),
29
+ const stressOption = Flag.boolean("stress").pipe(
30
+ Flag.withDescription("Use Wings stress binary variant"),
31
+ Flag.withDefault(false),
32
32
  );
33
33
 
34
- const yesOption = Options.boolean("yes").pipe(
35
- Options.withAlias("y"),
36
- Options.withDescription("Skip confirmation prompts"),
37
- Options.withDefault(false),
34
+ const yesOption = Flag.boolean("yes").pipe(
35
+ Flag.withAlias("y"),
36
+ Flag.withDescription("Skip confirmation prompts"),
37
+ Flag.withDefault(false),
38
38
  );
39
39
 
40
40
  export const devCommand = Command.make(
@@ -53,7 +53,7 @@ export const devCommand = Command.make(
53
53
  options.docker ? runWithDocker(options) : runWithBinary(options),
54
54
  catch: (error) =>
55
55
  error instanceof Error ? error : new Error("Operation failed"),
56
- }).pipe(Effect.catchAll(handleCliError("Operation failed"))),
56
+ }).pipe(Effect.catch(handleCliError("Operation failed"))),
57
57
  ).pipe(
58
58
  Command.withDescription(
59
59
  "Download and run Wings dev server locally (Docker recommended for portability)",
@@ -221,8 +221,8 @@ async function runWithDocker(options: { tag: string; forcePull: boolean }) {
221
221
  "7780:7780",
222
222
  image,
223
223
  "dev",
224
- "--http-address=0.0.0.0:7780",
225
- "--metadata-address=0.0.0.0:7777",
224
+ "--http.address=0.0.0.0:7780",
225
+ "--metadata.address=0.0.0.0:7777",
226
226
  ],
227
227
  {
228
228
  stdout: "inherit",