@eide/foir-cli 0.4.2 → 0.4.4

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/dist/cli.js CHANGED
@@ -1144,7 +1144,8 @@ function createIdentityMethods(client) {
1144
1144
  }
1145
1145
 
1146
1146
  // src/lib/rpc/models.ts
1147
- import { create as create2 } from "@bufbuild/protobuf";
1147
+ import { create as create2, fromJson } from "@bufbuild/protobuf";
1148
+ import { ValueSchema } from "@bufbuild/protobuf/wkt";
1148
1149
  import {
1149
1150
  FieldSchema as ProtoFieldSchema,
1150
1151
  SharingConfigSchema as ProtoSharingConfigSchema,
@@ -1168,6 +1169,7 @@ function jsFieldToProto(f) {
1168
1169
  required: f.required,
1169
1170
  helpText: f.helpText,
1170
1171
  placeholder: f.placeholder,
1172
+ defaultValue: f.defaultValue !== void 0 ? fromJson(ValueSchema, f.defaultValue) : void 0,
1171
1173
  config: f.config && Object.keys(f.config).length > 0 ? f.config : void 0,
1172
1174
  itemType: f.itemType,
1173
1175
  storage: f.storage,
@@ -1887,7 +1889,12 @@ function createConfigsMethods(client) {
1887
1889
  }
1888
1890
 
1889
1891
  // src/lib/rpc/segments.ts
1890
- import { create as create5 } from "@bufbuild/protobuf";
1892
+ import { create as create5, fromJson as fromJson2 } from "@bufbuild/protobuf";
1893
+ import { ValueSchema as ValueSchema2 } from "@bufbuild/protobuf/wkt";
1894
+ import {
1895
+ RuleExpressionSchema,
1896
+ RuleOperandSchema
1897
+ } from "@eide/foir-proto-ts/expressions/v1/expressions_pb";
1891
1898
  import {
1892
1899
  CreateSegmentRequestSchema,
1893
1900
  GetSegmentRequestSchema,
@@ -1902,6 +1909,27 @@ import {
1902
1909
  OptOutOfSegmentRequestSchema,
1903
1910
  OptBackIntoSegmentRequestSchema
1904
1911
  } from "@eide/foir-proto-ts/segments/v1/segments_pb";
1912
+ function rawOperandToProto(raw) {
1913
+ return create5(RuleOperandSchema, {
1914
+ type: raw.type,
1915
+ path: raw.path,
1916
+ value: raw.value !== void 0 ? fromJson2(ValueSchema2, raw.value) : void 0,
1917
+ valueType: raw.valueType,
1918
+ label: raw.label
1919
+ });
1920
+ }
1921
+ function rawRulesToProto(raw) {
1922
+ return create5(RuleExpressionSchema, {
1923
+ type: raw.type,
1924
+ id: raw.id,
1925
+ left: raw.left ? rawOperandToProto(raw.left) : void 0,
1926
+ operator: raw.operator,
1927
+ right: raw.right ? rawOperandToProto(raw.right) : void 0,
1928
+ conditions: Array.isArray(raw.conditions) ? raw.conditions.map((c) => rawRulesToProto(c)) : [],
1929
+ logicalOperator: raw.logicalOperator,
1930
+ value: raw.value !== void 0 ? fromJson2(ValueSchema2, raw.value) : void 0
1931
+ });
1932
+ }
1905
1933
  function createSegmentsMethods(client) {
1906
1934
  return {
1907
1935
  // ── Queries ──────────────────────────────────────────────
@@ -1928,12 +1956,13 @@ function createSegmentsMethods(client) {
1928
1956
  },
1929
1957
  // ── Mutations ────────────────────────────────────────────
1930
1958
  async createSegment(params) {
1959
+ const rules = params.rules && !("$typeName" in params.rules) ? rawRulesToProto(params.rules) : params.rules;
1931
1960
  const resp = await client.createSegment(
1932
1961
  create5(CreateSegmentRequestSchema, {
1933
1962
  key: params.key,
1934
1963
  name: params.name,
1935
1964
  description: params.description,
1936
- rules: params.rules,
1965
+ rules,
1937
1966
  evaluationMode: params.evaluationMode,
1938
1967
  isActive: params.isActive
1939
1968
  })
@@ -1941,12 +1970,13 @@ function createSegmentsMethods(client) {
1941
1970
  return resp.segment ?? null;
1942
1971
  },
1943
1972
  async updateSegment(params) {
1973
+ const rules = params.rules && !("$typeName" in params.rules) ? rawRulesToProto(params.rules) : params.rules;
1944
1974
  const resp = await client.updateSegment(
1945
1975
  create5(UpdateSegmentRequestSchema, {
1946
1976
  id: params.id,
1947
1977
  name: params.name,
1948
1978
  description: params.description,
1949
- rules: params.rules,
1979
+ rules,
1950
1980
  evaluationMode: params.evaluationMode,
1951
1981
  isActive: params.isActive
1952
1982
  })
@@ -2708,7 +2738,8 @@ import {
2708
2738
  GetOperationRequestSchema,
2709
2739
  CreateOperationRequestSchema,
2710
2740
  UpdateOperationRequestSchema,
2711
- DeleteOperationRequestSchema
2741
+ DeleteOperationRequestSchema,
2742
+ GetSigningSecretRequestSchema
2712
2743
  } from "@eide/foir-proto-ts/operations/v1/operations_pb";
2713
2744
  function createOperationsMethods(client) {
2714
2745
  return {
@@ -2743,10 +2774,14 @@ function createOperationsMethods(client) {
2743
2774
  description: params.description,
2744
2775
  icon: params.icon,
2745
2776
  category: params.category,
2746
- endpointAuth: params.endpointAuth,
2747
2777
  timeoutMs: params.timeoutMs,
2748
2778
  inputSchema: params.inputSchema,
2749
2779
  outputSchema: params.outputSchema,
2780
+ streamConfig: params.streamConfig,
2781
+ quotas: params.quotas,
2782
+ retryPolicy: params.retryPolicy,
2783
+ allowedRoles: params.allowedRoles ?? [],
2784
+ precondition: params.precondition,
2750
2785
  configId: params.configId
2751
2786
  })
2752
2787
  );
@@ -2759,10 +2794,13 @@ function createOperationsMethods(client) {
2759
2794
  name: params.name,
2760
2795
  description: params.description,
2761
2796
  endpoint: params.endpoint,
2762
- endpointAuth: params.endpointAuth,
2763
2797
  timeoutMs: params.timeoutMs,
2764
2798
  inputSchema: params.inputSchema,
2765
2799
  outputSchema: params.outputSchema,
2800
+ streamConfig: params.streamConfig,
2801
+ quotas: params.quotas,
2802
+ retryPolicy: params.retryPolicy,
2803
+ precondition: params.precondition,
2766
2804
  isActive: params.isActive
2767
2805
  })
2768
2806
  );
@@ -2773,6 +2811,12 @@ function createOperationsMethods(client) {
2773
2811
  create9(DeleteOperationRequestSchema, { id })
2774
2812
  );
2775
2813
  return resp.success;
2814
+ },
2815
+ async getSigningSecret() {
2816
+ const resp = await client.getSigningSecret(
2817
+ create9(GetSigningSecretRequestSchema, {})
2818
+ );
2819
+ return { secret: resp.secret, prefix: resp.prefix };
2776
2820
  }
2777
2821
  };
2778
2822
  }
@@ -2926,6 +2970,7 @@ function createCronSchedulesMethods(client) {
2926
2970
  create11(CreateCronScheduleRequestSchema, {
2927
2971
  key: params.key,
2928
2972
  name: params.name,
2973
+ description: params.description,
2929
2974
  cron: params.cron,
2930
2975
  timezone: params.timezone,
2931
2976
  operationKey: params.operationKey,
@@ -4306,7 +4351,7 @@ import chalk5 from "chalk";
4306
4351
  import inquirer4 from "inquirer";
4307
4352
  var FIELD_DEFAULTS = {
4308
4353
  text: "",
4309
- richtext: "",
4354
+ content: "",
4310
4355
  number: 0,
4311
4356
  boolean: false,
4312
4357
  date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
@@ -4341,7 +4386,7 @@ function generateModelTemplate(key) {
4341
4386
  },
4342
4387
  {
4343
4388
  key: "description",
4344
- type: "richtext",
4389
+ type: "content",
4345
4390
  label: "Description"
4346
4391
  },
4347
4392
  {
@@ -4531,8 +4576,9 @@ async function reconcileConfig(client, configId, manifest) {
4531
4576
  profileSchemaUpdated: false,
4532
4577
  apiKeys: []
4533
4578
  };
4579
+ const operationBaseUrl = manifest.operationBaseUrl ?? "";
4534
4580
  await reconcileModels(client, configId, manifest.models ?? [], summary);
4535
- await reconcileOperations(client, configId, manifest.operations ?? [], summary);
4581
+ await reconcileOperations(client, configId, manifest.operations ?? [], operationBaseUrl, summary);
4536
4582
  await reconcileHooks(client, configId, manifest.hooks ?? [], summary);
4537
4583
  await reconcileSegments(client, manifest.segments ?? [], summary);
4538
4584
  await reconcileCronSchedules(client, configId, manifest.schedules ?? [], summary);
@@ -4553,13 +4599,16 @@ async function reconcileModels(client, configId, models, summary) {
4553
4599
  for (const m of models) {
4554
4600
  if (!m.key || !m.name) continue;
4555
4601
  manifestKeys.add(m.key);
4602
+ const config2 = { ...m.config };
4603
+ if (m.pluralName) config2.pluralName = m.pluralName;
4604
+ if (m.description) config2.description = m.description;
4556
4605
  const ex = existingByKey.get(m.key);
4557
4606
  if (ex) {
4558
4607
  await client.models.updateModel({
4559
4608
  id: ex.id,
4560
4609
  name: m.name,
4561
4610
  fields: m.fields,
4562
- config: m.config
4611
+ config: config2
4563
4612
  });
4564
4613
  summary.models.updated++;
4565
4614
  } else {
@@ -4567,7 +4616,7 @@ async function reconcileModels(client, configId, models, summary) {
4567
4616
  key: m.key,
4568
4617
  name: m.name,
4569
4618
  fields: m.fields,
4570
- config: m.config,
4619
+ config: config2,
4571
4620
  configId
4572
4621
  });
4573
4622
  summary.models.created++;
@@ -4582,7 +4631,12 @@ async function reconcileModels(client, configId, models, summary) {
4582
4631
  }
4583
4632
  }
4584
4633
  }
4585
- async function reconcileOperations(client, configId, operations, summary) {
4634
+ function resolveEndpoint(endpoint, baseUrl) {
4635
+ if (!endpoint) return "";
4636
+ if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) return endpoint;
4637
+ return baseUrl ? `${baseUrl.replace(/\/+$/, "")}${endpoint.startsWith("/") ? "" : "/"}${endpoint}` : endpoint;
4638
+ }
4639
+ async function reconcileOperations(client, configId, operations, operationBaseUrl, summary) {
4586
4640
  const existing = await client.operations.listOperations({ configId, limit: 200 });
4587
4641
  const existingByKey = new Map(
4588
4642
  (existing.operations ?? []).map((o) => [o.key, o])
@@ -4592,21 +4646,39 @@ async function reconcileOperations(client, configId, operations, summary) {
4592
4646
  if (!op.key || !op.name) continue;
4593
4647
  manifestKeys.add(op.key);
4594
4648
  const ex = existingByKey.get(op.key);
4649
+ const endpoint = resolveEndpoint(op.endpoint, operationBaseUrl);
4595
4650
  if (ex) {
4596
4651
  await client.operations.updateOperation({
4597
4652
  id: ex.id,
4598
4653
  name: op.name,
4599
4654
  description: op.description,
4600
- endpoint: op.endpoint
4655
+ endpoint,
4656
+ timeoutMs: op.timeoutMs,
4657
+ inputSchema: op.inputSchema,
4658
+ outputSchema: op.outputSchema,
4659
+ streamConfig: op.streamConfig,
4660
+ quotas: op.quotas,
4661
+ retryPolicy: op.retryPolicy,
4662
+ precondition: op.precondition,
4663
+ isActive: op.isActive
4601
4664
  });
4602
4665
  summary.operations.updated++;
4603
4666
  } else {
4604
4667
  await client.operations.createOperation({
4605
4668
  key: op.key,
4606
4669
  name: op.name,
4607
- endpoint: op.endpoint ?? "",
4670
+ endpoint,
4608
4671
  description: op.description,
4672
+ icon: op.icon,
4609
4673
  category: op.category,
4674
+ timeoutMs: op.timeoutMs,
4675
+ inputSchema: op.inputSchema,
4676
+ outputSchema: op.outputSchema,
4677
+ streamConfig: op.streamConfig,
4678
+ quotas: op.quotas,
4679
+ retryPolicy: op.retryPolicy,
4680
+ allowedRoles: op.allowedRoles,
4681
+ precondition: op.precondition,
4610
4682
  configId
4611
4683
  });
4612
4684
  summary.operations.created++;
@@ -4637,18 +4709,23 @@ async function reconcileHooks(client, configId, hooks, summary) {
4637
4709
  await client.hooks.updateHook({
4638
4710
  id: ex.id,
4639
4711
  name,
4712
+ description: hook.description,
4640
4713
  operationKey: hook.operationKey,
4641
- filter: hook.filter
4714
+ filter: hook.filter,
4715
+ notificationConfig: hook.notificationConfig,
4716
+ isActive: hook.isActive
4642
4717
  });
4643
4718
  summary.hooks.updated++;
4644
4719
  } else {
4645
4720
  await client.hooks.createHook({
4646
4721
  key,
4647
4722
  name,
4723
+ description: hook.description,
4648
4724
  event: hook.event,
4649
4725
  targetType: hook.type ?? "operation",
4650
4726
  operationKey: hook.operationKey,
4651
4727
  filter: hook.filter,
4728
+ notificationConfig: hook.notificationConfig,
4652
4729
  configId
4653
4730
  });
4654
4731
  summary.hooks.created++;
@@ -4702,23 +4779,27 @@ async function reconcileCronSchedules(client, configId, schedules, summary) {
4702
4779
  );
4703
4780
  const manifestKeys = /* @__PURE__ */ new Set();
4704
4781
  for (const sched of schedules) {
4705
- const key = sched.operationKey;
4782
+ const key = sched.key ?? sched.operationKey;
4706
4783
  if (!key || !sched.cron) continue;
4707
4784
  manifestKeys.add(key);
4785
+ const name = sched.name ?? key;
4708
4786
  const ex = existingByKey.get(key);
4709
4787
  if (ex) {
4710
4788
  await client.cronSchedules.updateCronSchedule({
4711
4789
  id: ex.id,
4712
- name: key,
4790
+ name,
4791
+ description: sched.description,
4713
4792
  cron: sched.cron,
4714
4793
  timezone: sched.timezone,
4715
- operationKey: sched.operationKey
4794
+ operationKey: sched.operationKey,
4795
+ isActive: sched.enabled
4716
4796
  });
4717
4797
  summary.cronSchedules.updated++;
4718
4798
  } else {
4719
4799
  await client.cronSchedules.createCronSchedule({
4720
4800
  key,
4721
- name: key,
4801
+ name,
4802
+ description: sched.description,
4722
4803
  cron: sched.cron,
4723
4804
  timezone: sched.timezone,
4724
4805
  operationKey: sched.operationKey,
@@ -4911,11 +4992,11 @@ function registerPushCommand(program2, globalOpts) {
4911
4992
  label: `${pk.name} (${pk.keyType})`
4912
4993
  });
4913
4994
  }
4914
- const webhookSecret = applyResult.webhookSecret;
4915
- if (webhookSecret) {
4995
+ const { secret: signingSecret } = await client.operations.getSigningSecret();
4996
+ if (signingSecret) {
4916
4997
  envWrites.push({
4917
4998
  key: "FOIR_WEBHOOK_SECRET",
4918
- value: webhookSecret,
4999
+ value: signingSecret,
4919
5000
  label: "Webhook signing secret"
4920
5001
  });
4921
5002
  }
@@ -33,6 +33,8 @@ interface FieldDefinitionInput {
33
33
  interface ApplyConfigModelInput {
34
34
  key: string;
35
35
  name: string;
36
+ pluralName?: string;
37
+ description?: string;
36
38
  fields?: FieldDefinitionInput[];
37
39
  config?: Record<string, unknown>;
38
40
  }
@@ -40,11 +42,24 @@ interface ApplyConfigOperationInput {
40
42
  key: string;
41
43
  name: string;
42
44
  description?: string;
45
+ icon?: string;
43
46
  category?: string;
44
47
  /** HTTP endpoint URL that the platform calls when this operation is triggered. */
45
48
  endpoint?: string;
46
- config?: Record<string, unknown>;
47
49
  isActive?: boolean;
50
+ timeoutMs?: number;
51
+ inputSchema?: Record<string, unknown>;
52
+ outputSchema?: Record<string, unknown>;
53
+ /** Streaming configuration for SSE/chunked responses. */
54
+ streamConfig?: Record<string, unknown>;
55
+ /** Usage quota rules (e.g., rate limits per customer). */
56
+ quotas?: Record<string, unknown>;
57
+ /** Retry policy for failed executions. */
58
+ retryPolicy?: Record<string, unknown>;
59
+ /** Roles allowed to execute this operation. */
60
+ allowedRoles?: string[];
61
+ /** Precondition that must be met before execution (e.g., segment membership). */
62
+ precondition?: Record<string, unknown>;
48
63
  }
49
64
  interface ApplyConfigSegmentInput {
50
65
  key: string;
@@ -55,11 +70,15 @@ interface ApplyConfigSegmentInput {
55
70
  isActive?: boolean;
56
71
  }
57
72
  interface ApplyConfigScheduleInput {
73
+ /** Unique key for this schedule. Defaults to operationKey if not provided. */
74
+ key?: string;
75
+ /** Display name. Defaults to key if not provided. */
76
+ name?: string;
77
+ description?: string;
58
78
  operationKey: string;
59
79
  cron: string;
60
80
  timezone?: string;
61
81
  enabled?: boolean;
62
- payload?: Record<string, unknown>;
63
82
  }
64
83
  interface ApplyConfigAuthProviderInput {
65
84
  key: string;
@@ -84,20 +103,20 @@ interface ApplyConfigHookInput {
84
103
  key?: string;
85
104
  /** Display name. */
86
105
  name?: string;
106
+ /** Description of what this hook does. */
107
+ description?: string;
87
108
  /** Lifecycle event that triggers this hook. */
88
109
  event: string;
110
+ /** Target type — defaults to 'operation'. */
111
+ type?: string;
89
112
  /** Key of the operation to execute. */
90
113
  operationKey?: string;
91
114
  /** Filter to scope the hook (e.g., `{ modelKey: 'redirect' }`). */
92
115
  filter?: Record<string, unknown>;
93
- type?: string;
94
- url?: string;
95
- method?: string;
96
- async?: boolean;
97
- headers?: Record<string, string>;
98
- additionalData?: Record<string, unknown>;
99
- expression?: Record<string, unknown>;
100
- hooks?: ApplyConfigHookInput[];
116
+ /** Notification config for notification-type hooks. */
117
+ notificationConfig?: Record<string, unknown>;
118
+ /** Whether the hook is active. Defaults to true. */
119
+ isActive?: boolean;
101
120
  }
102
121
  interface ApplyConfigApiKeyInput {
103
122
  /** Name for this API key (e.g. "Tilly iOS", "Tilly BFF"). */
@@ -118,6 +137,8 @@ interface ApplyConfigInput {
118
137
  name: string;
119
138
  configType?: string;
120
139
  force?: boolean;
140
+ /** Base URL prepended to relative operation endpoints. */
141
+ operationBaseUrl?: string;
121
142
  models?: ApplyConfigModelInput[];
122
143
  operations?: ApplyConfigOperationInput[];
123
144
  segments?: ApplyConfigSegmentInput[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eide/foir-cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Universal platform CLI for Foir platform",
5
5
  "type": "module",
6
6
  "publishConfig": {