@eide/foir-cli 0.4.1 → 0.4.3
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 +800 -80
- package/dist/lib/config-helpers.d.ts +38 -12
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -464,6 +464,9 @@ import { SegmentsService as SegmentsService2 } from "@eide/foir-proto-ts/segment
|
|
|
464
464
|
import { ExperimentsService as ExperimentsService2 } from "@eide/foir-proto-ts/experiments/v1/experiments_pb";
|
|
465
465
|
import { SettingsService as SettingsService2 } from "@eide/foir-proto-ts/settings/v1/settings_pb";
|
|
466
466
|
import { StorageService as StorageService2 } from "@eide/foir-proto-ts/storage/v1/storage_pb";
|
|
467
|
+
import { OperationsService as OperationsService2 } from "@eide/foir-proto-ts/operations/v1/operations_pb";
|
|
468
|
+
import { HooksService as HooksService2 } from "@eide/foir-proto-ts/hooks/v1/hooks_pb";
|
|
469
|
+
import { SchedulesService as SchedulesService2 } from "@eide/foir-proto-ts/schedules/v1/schedules_pb";
|
|
467
470
|
|
|
468
471
|
// src/lib/rpc/identity.ts
|
|
469
472
|
import { create } from "@bufbuild/protobuf";
|
|
@@ -937,13 +940,18 @@ function createIdentityMethods(client) {
|
|
|
937
940
|
},
|
|
938
941
|
// ── API Keys ──────────────────────────────────────────
|
|
939
942
|
async createApiKey(params) {
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
943
|
+
const req = create(CreateApiKeyRequestSchema, {
|
|
944
|
+
name: params.name,
|
|
945
|
+
keyType: params.keyType,
|
|
946
|
+
rateLimitPerHour: params.rateLimitPerHour
|
|
947
|
+
});
|
|
948
|
+
if (params.allowedModels?.length) {
|
|
949
|
+
req.allowedModels = params.allowedModels;
|
|
950
|
+
}
|
|
951
|
+
if (params.allowedFileTypes?.length) {
|
|
952
|
+
req.allowedFileTypes = params.allowedFileTypes;
|
|
953
|
+
}
|
|
954
|
+
const resp = await client.createApiKey(req);
|
|
947
955
|
return { apiKey: resp.apiKey ?? null };
|
|
948
956
|
},
|
|
949
957
|
async getApiKey(id) {
|
|
@@ -1136,7 +1144,8 @@ function createIdentityMethods(client) {
|
|
|
1136
1144
|
}
|
|
1137
1145
|
|
|
1138
1146
|
// src/lib/rpc/models.ts
|
|
1139
|
-
import { create as create2 } from "@bufbuild/protobuf";
|
|
1147
|
+
import { create as create2, fromJson } from "@bufbuild/protobuf";
|
|
1148
|
+
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
1140
1149
|
import {
|
|
1141
1150
|
FieldSchema as ProtoFieldSchema,
|
|
1142
1151
|
SharingConfigSchema as ProtoSharingConfigSchema,
|
|
@@ -1160,6 +1169,7 @@ function jsFieldToProto(f) {
|
|
|
1160
1169
|
required: f.required,
|
|
1161
1170
|
helpText: f.helpText,
|
|
1162
1171
|
placeholder: f.placeholder,
|
|
1172
|
+
defaultValue: f.defaultValue !== void 0 ? fromJson(ValueSchema, f.defaultValue) : void 0,
|
|
1163
1173
|
config: f.config && Object.keys(f.config).length > 0 ? f.config : void 0,
|
|
1164
1174
|
itemType: f.itemType,
|
|
1165
1175
|
storage: f.storage,
|
|
@@ -1879,7 +1889,12 @@ function createConfigsMethods(client) {
|
|
|
1879
1889
|
}
|
|
1880
1890
|
|
|
1881
1891
|
// src/lib/rpc/segments.ts
|
|
1882
|
-
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";
|
|
1883
1898
|
import {
|
|
1884
1899
|
CreateSegmentRequestSchema,
|
|
1885
1900
|
GetSegmentRequestSchema,
|
|
@@ -1894,6 +1909,27 @@ import {
|
|
|
1894
1909
|
OptOutOfSegmentRequestSchema,
|
|
1895
1910
|
OptBackIntoSegmentRequestSchema
|
|
1896
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
|
+
}
|
|
1897
1933
|
function createSegmentsMethods(client) {
|
|
1898
1934
|
return {
|
|
1899
1935
|
// ── Queries ──────────────────────────────────────────────
|
|
@@ -1920,12 +1956,13 @@ function createSegmentsMethods(client) {
|
|
|
1920
1956
|
},
|
|
1921
1957
|
// ── Mutations ────────────────────────────────────────────
|
|
1922
1958
|
async createSegment(params) {
|
|
1959
|
+
const rules = params.rules && !("$typeName" in params.rules) ? rawRulesToProto(params.rules) : params.rules;
|
|
1923
1960
|
const resp = await client.createSegment(
|
|
1924
1961
|
create5(CreateSegmentRequestSchema, {
|
|
1925
1962
|
key: params.key,
|
|
1926
1963
|
name: params.name,
|
|
1927
1964
|
description: params.description,
|
|
1928
|
-
rules
|
|
1965
|
+
rules,
|
|
1929
1966
|
evaluationMode: params.evaluationMode,
|
|
1930
1967
|
isActive: params.isActive
|
|
1931
1968
|
})
|
|
@@ -1933,12 +1970,13 @@ function createSegmentsMethods(client) {
|
|
|
1933
1970
|
return resp.segment ?? null;
|
|
1934
1971
|
},
|
|
1935
1972
|
async updateSegment(params) {
|
|
1973
|
+
const rules = params.rules && !("$typeName" in params.rules) ? rawRulesToProto(params.rules) : params.rules;
|
|
1936
1974
|
const resp = await client.updateSegment(
|
|
1937
1975
|
create5(UpdateSegmentRequestSchema, {
|
|
1938
1976
|
id: params.id,
|
|
1939
1977
|
name: params.name,
|
|
1940
1978
|
description: params.description,
|
|
1941
|
-
rules
|
|
1979
|
+
rules,
|
|
1942
1980
|
evaluationMode: params.evaluationMode,
|
|
1943
1981
|
isActive: params.isActive
|
|
1944
1982
|
})
|
|
@@ -2693,6 +2731,279 @@ function createStorageMethods(client) {
|
|
|
2693
2731
|
};
|
|
2694
2732
|
}
|
|
2695
2733
|
|
|
2734
|
+
// src/lib/rpc/operations.ts
|
|
2735
|
+
import { create as create9 } from "@bufbuild/protobuf";
|
|
2736
|
+
import {
|
|
2737
|
+
ListOperationsRequestSchema as ListOperationsRequestSchema2,
|
|
2738
|
+
GetOperationRequestSchema,
|
|
2739
|
+
CreateOperationRequestSchema,
|
|
2740
|
+
UpdateOperationRequestSchema,
|
|
2741
|
+
DeleteOperationRequestSchema,
|
|
2742
|
+
GetSigningSecretRequestSchema
|
|
2743
|
+
} from "@eide/foir-proto-ts/operations/v1/operations_pb";
|
|
2744
|
+
function createOperationsMethods(client) {
|
|
2745
|
+
return {
|
|
2746
|
+
// ── CRUD ──────────────────────────────────────────────────
|
|
2747
|
+
async listOperations(params = {}) {
|
|
2748
|
+
return client.listOperations(
|
|
2749
|
+
create9(ListOperationsRequestSchema2, {
|
|
2750
|
+
configId: params.configId,
|
|
2751
|
+
category: params.category,
|
|
2752
|
+
isActive: params.isActive,
|
|
2753
|
+
search: params.search,
|
|
2754
|
+
limit: params.limit ?? 50,
|
|
2755
|
+
offset: params.offset ?? 0
|
|
2756
|
+
})
|
|
2757
|
+
);
|
|
2758
|
+
},
|
|
2759
|
+
async getOperation(params) {
|
|
2760
|
+
const resp = await client.getOperation(
|
|
2761
|
+
create9(GetOperationRequestSchema, {
|
|
2762
|
+
id: params.id ?? "",
|
|
2763
|
+
key: params.key
|
|
2764
|
+
})
|
|
2765
|
+
);
|
|
2766
|
+
return resp.operation ?? null;
|
|
2767
|
+
},
|
|
2768
|
+
async createOperation(params) {
|
|
2769
|
+
const resp = await client.createOperation(
|
|
2770
|
+
create9(CreateOperationRequestSchema, {
|
|
2771
|
+
key: params.key,
|
|
2772
|
+
name: params.name,
|
|
2773
|
+
endpoint: params.endpoint,
|
|
2774
|
+
description: params.description,
|
|
2775
|
+
icon: params.icon,
|
|
2776
|
+
category: params.category,
|
|
2777
|
+
timeoutMs: params.timeoutMs,
|
|
2778
|
+
inputSchema: params.inputSchema,
|
|
2779
|
+
outputSchema: params.outputSchema,
|
|
2780
|
+
streamConfig: params.streamConfig,
|
|
2781
|
+
quotas: params.quotas,
|
|
2782
|
+
retryPolicy: params.retryPolicy,
|
|
2783
|
+
allowedRoles: params.allowedRoles ?? [],
|
|
2784
|
+
precondition: params.precondition,
|
|
2785
|
+
configId: params.configId
|
|
2786
|
+
})
|
|
2787
|
+
);
|
|
2788
|
+
return resp.operation ?? null;
|
|
2789
|
+
},
|
|
2790
|
+
async updateOperation(params) {
|
|
2791
|
+
const resp = await client.updateOperation(
|
|
2792
|
+
create9(UpdateOperationRequestSchema, {
|
|
2793
|
+
id: params.id,
|
|
2794
|
+
name: params.name,
|
|
2795
|
+
description: params.description,
|
|
2796
|
+
endpoint: params.endpoint,
|
|
2797
|
+
timeoutMs: params.timeoutMs,
|
|
2798
|
+
inputSchema: params.inputSchema,
|
|
2799
|
+
outputSchema: params.outputSchema,
|
|
2800
|
+
streamConfig: params.streamConfig,
|
|
2801
|
+
quotas: params.quotas,
|
|
2802
|
+
retryPolicy: params.retryPolicy,
|
|
2803
|
+
precondition: params.precondition,
|
|
2804
|
+
isActive: params.isActive
|
|
2805
|
+
})
|
|
2806
|
+
);
|
|
2807
|
+
return resp.operation ?? null;
|
|
2808
|
+
},
|
|
2809
|
+
async deleteOperation(id) {
|
|
2810
|
+
const resp = await client.deleteOperation(
|
|
2811
|
+
create9(DeleteOperationRequestSchema, { id })
|
|
2812
|
+
);
|
|
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 };
|
|
2820
|
+
}
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
// src/lib/rpc/hooks.ts
|
|
2825
|
+
import { create as create10 } from "@bufbuild/protobuf";
|
|
2826
|
+
import {
|
|
2827
|
+
ListHooksRequestSchema,
|
|
2828
|
+
GetHookRequestSchema,
|
|
2829
|
+
GetHookByKeyRequestSchema,
|
|
2830
|
+
CreateHookRequestSchema,
|
|
2831
|
+
UpdateHookRequestSchema,
|
|
2832
|
+
DeleteHookRequestSchema,
|
|
2833
|
+
ListHookDeliveriesRequestSchema,
|
|
2834
|
+
RetryHookDeliveryRequestSchema,
|
|
2835
|
+
TestHookRequestSchema
|
|
2836
|
+
} from "@eide/foir-proto-ts/hooks/v1/hooks_pb";
|
|
2837
|
+
function createHooksMethods(client) {
|
|
2838
|
+
return {
|
|
2839
|
+
// ── Queries ──────────────────────────────────────────────
|
|
2840
|
+
async listHooks(params = {}) {
|
|
2841
|
+
return client.listHooks(
|
|
2842
|
+
create10(ListHooksRequestSchema, {
|
|
2843
|
+
event: params.event,
|
|
2844
|
+
isActive: params.isActive,
|
|
2845
|
+
configId: params.configId,
|
|
2846
|
+
limit: params.limit ?? 50,
|
|
2847
|
+
offset: params.offset ?? 0
|
|
2848
|
+
})
|
|
2849
|
+
);
|
|
2850
|
+
},
|
|
2851
|
+
async getHook(id) {
|
|
2852
|
+
const resp = await client.getHook(create10(GetHookRequestSchema, { id }));
|
|
2853
|
+
return resp.hook ?? null;
|
|
2854
|
+
},
|
|
2855
|
+
// ── Mutations ────────────────────────────────────────────
|
|
2856
|
+
async createHook(params) {
|
|
2857
|
+
const resp = await client.createHook(
|
|
2858
|
+
create10(CreateHookRequestSchema, {
|
|
2859
|
+
key: params.key,
|
|
2860
|
+
name: params.name,
|
|
2861
|
+
event: params.event,
|
|
2862
|
+
targetType: params.targetType,
|
|
2863
|
+
description: params.description,
|
|
2864
|
+
operationKey: params.operationKey,
|
|
2865
|
+
notificationConfig: params.notificationConfig ?? void 0,
|
|
2866
|
+
filter: params.filter ?? void 0,
|
|
2867
|
+
configId: params.configId
|
|
2868
|
+
})
|
|
2869
|
+
);
|
|
2870
|
+
return resp.hook ?? null;
|
|
2871
|
+
},
|
|
2872
|
+
async updateHook(params) {
|
|
2873
|
+
const resp = await client.updateHook(
|
|
2874
|
+
create10(UpdateHookRequestSchema, {
|
|
2875
|
+
id: params.id,
|
|
2876
|
+
name: params.name,
|
|
2877
|
+
description: params.description,
|
|
2878
|
+
operationKey: params.operationKey,
|
|
2879
|
+
notificationConfig: params.notificationConfig ?? void 0,
|
|
2880
|
+
filter: params.filter ?? void 0,
|
|
2881
|
+
isActive: params.isActive
|
|
2882
|
+
})
|
|
2883
|
+
);
|
|
2884
|
+
return resp.hook ?? null;
|
|
2885
|
+
},
|
|
2886
|
+
async deleteHook(id) {
|
|
2887
|
+
const resp = await client.deleteHook(
|
|
2888
|
+
create10(DeleteHookRequestSchema, { id })
|
|
2889
|
+
);
|
|
2890
|
+
return resp.success;
|
|
2891
|
+
},
|
|
2892
|
+
// ── Get by Key ──────────────────────────────────────────
|
|
2893
|
+
async getHookByKey(key) {
|
|
2894
|
+
const resp = await client.getHookByKey(
|
|
2895
|
+
create10(GetHookByKeyRequestSchema, { key })
|
|
2896
|
+
);
|
|
2897
|
+
return resp.hook ?? null;
|
|
2898
|
+
},
|
|
2899
|
+
// ── Deliveries ──────────────────────────────────────────
|
|
2900
|
+
async listHookDeliveries(params) {
|
|
2901
|
+
return client.listHookDeliveries(
|
|
2902
|
+
create10(ListHookDeliveriesRequestSchema, {
|
|
2903
|
+
hookId: params.hookId,
|
|
2904
|
+
status: params.status,
|
|
2905
|
+
limit: params.limit ?? 50,
|
|
2906
|
+
offset: params.offset ?? 0
|
|
2907
|
+
})
|
|
2908
|
+
);
|
|
2909
|
+
},
|
|
2910
|
+
async retryHookDelivery(deliveryId) {
|
|
2911
|
+
const resp = await client.retryHookDelivery(
|
|
2912
|
+
create10(RetryHookDeliveryRequestSchema, { deliveryId })
|
|
2913
|
+
);
|
|
2914
|
+
return resp.success;
|
|
2915
|
+
},
|
|
2916
|
+
// ── Testing ─────────────────────────────────────────────
|
|
2917
|
+
async testHook(params) {
|
|
2918
|
+
const resp = await client.testHook(
|
|
2919
|
+
create10(TestHookRequestSchema, {
|
|
2920
|
+
hookId: params.hookId,
|
|
2921
|
+
testPayload: params.testPayload
|
|
2922
|
+
})
|
|
2923
|
+
);
|
|
2924
|
+
return {
|
|
2925
|
+
success: resp.success,
|
|
2926
|
+
error: resp.error ?? void 0
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
// src/lib/rpc/cron-schedules.ts
|
|
2933
|
+
import { create as create11 } from "@bufbuild/protobuf";
|
|
2934
|
+
import {
|
|
2935
|
+
ListCronSchedulesRequestSchema,
|
|
2936
|
+
GetCronScheduleRequestSchema,
|
|
2937
|
+
GetCronScheduleByKeyRequestSchema,
|
|
2938
|
+
CreateCronScheduleRequestSchema,
|
|
2939
|
+
UpdateCronScheduleRequestSchema,
|
|
2940
|
+
DeleteCronScheduleRequestSchema
|
|
2941
|
+
} from "@eide/foir-proto-ts/schedules/v1/schedules_pb";
|
|
2942
|
+
function createCronSchedulesMethods(client) {
|
|
2943
|
+
return {
|
|
2944
|
+
// ── Queries ──────────────────────────────────────────────
|
|
2945
|
+
async listCronSchedules(params = {}) {
|
|
2946
|
+
return client.listCronSchedules(
|
|
2947
|
+
create11(ListCronSchedulesRequestSchema, {
|
|
2948
|
+
configId: params.configId,
|
|
2949
|
+
isActive: params.isActive,
|
|
2950
|
+
limit: params.limit ?? 50,
|
|
2951
|
+
offset: params.offset ?? 0
|
|
2952
|
+
})
|
|
2953
|
+
);
|
|
2954
|
+
},
|
|
2955
|
+
async getCronSchedule(id) {
|
|
2956
|
+
const resp = await client.getCronSchedule(
|
|
2957
|
+
create11(GetCronScheduleRequestSchema, { id })
|
|
2958
|
+
);
|
|
2959
|
+
return resp.schedule ?? null;
|
|
2960
|
+
},
|
|
2961
|
+
async getCronScheduleByKey(key) {
|
|
2962
|
+
const resp = await client.getCronScheduleByKey(
|
|
2963
|
+
create11(GetCronScheduleByKeyRequestSchema, { key })
|
|
2964
|
+
);
|
|
2965
|
+
return resp.schedule ?? null;
|
|
2966
|
+
},
|
|
2967
|
+
// ── Mutations ────────────────────────────────────────────
|
|
2968
|
+
async createCronSchedule(params) {
|
|
2969
|
+
const resp = await client.createCronSchedule(
|
|
2970
|
+
create11(CreateCronScheduleRequestSchema, {
|
|
2971
|
+
key: params.key,
|
|
2972
|
+
name: params.name,
|
|
2973
|
+
description: params.description,
|
|
2974
|
+
cron: params.cron,
|
|
2975
|
+
timezone: params.timezone,
|
|
2976
|
+
operationKey: params.operationKey,
|
|
2977
|
+
configId: params.configId,
|
|
2978
|
+
targetType: params.targetType,
|
|
2979
|
+
targetConfig: params.targetConfig
|
|
2980
|
+
})
|
|
2981
|
+
);
|
|
2982
|
+
return resp.schedule ?? null;
|
|
2983
|
+
},
|
|
2984
|
+
async updateCronSchedule(params) {
|
|
2985
|
+
const resp = await client.updateCronSchedule(
|
|
2986
|
+
create11(UpdateCronScheduleRequestSchema, {
|
|
2987
|
+
id: params.id,
|
|
2988
|
+
name: params.name,
|
|
2989
|
+
description: params.description,
|
|
2990
|
+
cron: params.cron,
|
|
2991
|
+
timezone: params.timezone,
|
|
2992
|
+
operationKey: params.operationKey,
|
|
2993
|
+
isActive: params.isActive
|
|
2994
|
+
})
|
|
2995
|
+
);
|
|
2996
|
+
return resp.schedule ?? null;
|
|
2997
|
+
},
|
|
2998
|
+
async deleteCronSchedule(id) {
|
|
2999
|
+
const resp = await client.deleteCronSchedule(
|
|
3000
|
+
create11(DeleteCronScheduleRequestSchema, { id })
|
|
3001
|
+
);
|
|
3002
|
+
return resp.success;
|
|
3003
|
+
}
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
|
|
2696
3007
|
// src/lib/client.ts
|
|
2697
3008
|
import { GraphQLClient } from "graphql-request";
|
|
2698
3009
|
async function createPlatformClient(options) {
|
|
@@ -2739,7 +3050,14 @@ async function createPlatformClient(options) {
|
|
|
2739
3050
|
createRpcClient(ExperimentsService2, transport)
|
|
2740
3051
|
),
|
|
2741
3052
|
settings: createSettingsMethods(createRpcClient(SettingsService2, transport)),
|
|
2742
|
-
storage: createStorageMethods(createRpcClient(StorageService2, transport))
|
|
3053
|
+
storage: createStorageMethods(createRpcClient(StorageService2, transport)),
|
|
3054
|
+
operations: createOperationsMethods(
|
|
3055
|
+
createRpcClient(OperationsService2, transport)
|
|
3056
|
+
),
|
|
3057
|
+
hooks: createHooksMethods(createRpcClient(HooksService2, transport)),
|
|
3058
|
+
cronSchedules: createCronSchedulesMethods(
|
|
3059
|
+
createRpcClient(SchedulesService2, transport)
|
|
3060
|
+
)
|
|
2743
3061
|
};
|
|
2744
3062
|
}
|
|
2745
3063
|
function createPlatformClientWithHeaders(apiUrl, headers) {
|
|
@@ -2764,7 +3082,14 @@ function createPlatformClientWithHeaders(apiUrl, headers) {
|
|
|
2764
3082
|
createRpcClient(ExperimentsService2, transport)
|
|
2765
3083
|
),
|
|
2766
3084
|
settings: createSettingsMethods(createRpcClient(SettingsService2, transport)),
|
|
2767
|
-
storage: createStorageMethods(createRpcClient(StorageService2, transport))
|
|
3085
|
+
storage: createStorageMethods(createRpcClient(StorageService2, transport)),
|
|
3086
|
+
operations: createOperationsMethods(
|
|
3087
|
+
createRpcClient(OperationsService2, transport)
|
|
3088
|
+
),
|
|
3089
|
+
hooks: createHooksMethods(createRpcClient(HooksService2, transport)),
|
|
3090
|
+
cronSchedules: createCronSchedulesMethods(
|
|
3091
|
+
createRpcClient(SchedulesService2, transport)
|
|
3092
|
+
)
|
|
2768
3093
|
};
|
|
2769
3094
|
}
|
|
2770
3095
|
async function getStorageAuth(options) {
|
|
@@ -4026,7 +4351,7 @@ import chalk5 from "chalk";
|
|
|
4026
4351
|
import inquirer4 from "inquirer";
|
|
4027
4352
|
var FIELD_DEFAULTS = {
|
|
4028
4353
|
text: "",
|
|
4029
|
-
|
|
4354
|
+
content: "",
|
|
4030
4355
|
number: 0,
|
|
4031
4356
|
boolean: false,
|
|
4032
4357
|
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
@@ -4061,7 +4386,7 @@ function generateModelTemplate(key) {
|
|
|
4061
4386
|
},
|
|
4062
4387
|
{
|
|
4063
4388
|
key: "description",
|
|
4064
|
-
type: "
|
|
4389
|
+
type: "content",
|
|
4065
4390
|
label: "Description"
|
|
4066
4391
|
},
|
|
4067
4392
|
{
|
|
@@ -4235,6 +4560,332 @@ Edit the files, then run:
|
|
|
4235
4560
|
import chalk6 from "chalk";
|
|
4236
4561
|
import { existsSync as existsSync4, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4237
4562
|
import { resolve as resolve4 } from "path";
|
|
4563
|
+
|
|
4564
|
+
// src/lib/reconciler.ts
|
|
4565
|
+
function zeroCounts() {
|
|
4566
|
+
return { created: 0, updated: 0, deleted: 0 };
|
|
4567
|
+
}
|
|
4568
|
+
async function reconcileConfig(client, configId, manifest) {
|
|
4569
|
+
const summary = {
|
|
4570
|
+
models: zeroCounts(),
|
|
4571
|
+
operations: zeroCounts(),
|
|
4572
|
+
hooks: zeroCounts(),
|
|
4573
|
+
segments: zeroCounts(),
|
|
4574
|
+
cronSchedules: zeroCounts(),
|
|
4575
|
+
authProviders: zeroCounts(),
|
|
4576
|
+
profileSchemaUpdated: false,
|
|
4577
|
+
apiKeys: []
|
|
4578
|
+
};
|
|
4579
|
+
const operationBaseUrl = manifest.operationBaseUrl ?? "";
|
|
4580
|
+
await reconcileModels(client, configId, manifest.models ?? [], summary);
|
|
4581
|
+
await reconcileOperations(client, configId, manifest.operations ?? [], operationBaseUrl, summary);
|
|
4582
|
+
await reconcileHooks(client, configId, manifest.hooks ?? [], summary);
|
|
4583
|
+
await reconcileSegments(client, manifest.segments ?? [], summary);
|
|
4584
|
+
await reconcileCronSchedules(client, configId, manifest.schedules ?? [], summary);
|
|
4585
|
+
await reconcileAuthProviders(client, manifest.authProviders ?? [], summary);
|
|
4586
|
+
await reconcileProfileSchema(client, manifest, summary);
|
|
4587
|
+
await reconcileApiKeys(client, manifest.apiKeys ?? [], summary);
|
|
4588
|
+
return summary;
|
|
4589
|
+
}
|
|
4590
|
+
async function reconcileModels(client, configId, models, summary) {
|
|
4591
|
+
const existing = await client.models.listModels({ limit: 200 });
|
|
4592
|
+
const configOwned = existing.items.filter(
|
|
4593
|
+
(m) => m.configId === configId
|
|
4594
|
+
);
|
|
4595
|
+
const existingByKey = new Map(
|
|
4596
|
+
configOwned.map((m) => [m.key, m])
|
|
4597
|
+
);
|
|
4598
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4599
|
+
for (const m of models) {
|
|
4600
|
+
if (!m.key || !m.name) continue;
|
|
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;
|
|
4605
|
+
const ex = existingByKey.get(m.key);
|
|
4606
|
+
if (ex) {
|
|
4607
|
+
await client.models.updateModel({
|
|
4608
|
+
id: ex.id,
|
|
4609
|
+
name: m.name,
|
|
4610
|
+
fields: m.fields,
|
|
4611
|
+
config: config2
|
|
4612
|
+
});
|
|
4613
|
+
summary.models.updated++;
|
|
4614
|
+
} else {
|
|
4615
|
+
await client.models.createModel({
|
|
4616
|
+
key: m.key,
|
|
4617
|
+
name: m.name,
|
|
4618
|
+
fields: m.fields,
|
|
4619
|
+
config: config2,
|
|
4620
|
+
configId
|
|
4621
|
+
});
|
|
4622
|
+
summary.models.created++;
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
for (const [key, ex] of existingByKey) {
|
|
4626
|
+
if (!manifestKeys.has(key)) {
|
|
4627
|
+
await client.models.deleteModel(
|
|
4628
|
+
ex.id
|
|
4629
|
+
);
|
|
4630
|
+
summary.models.deleted++;
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
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) {
|
|
4640
|
+
const existing = await client.operations.listOperations({ configId, limit: 200 });
|
|
4641
|
+
const existingByKey = new Map(
|
|
4642
|
+
(existing.operations ?? []).map((o) => [o.key, o])
|
|
4643
|
+
);
|
|
4644
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4645
|
+
for (const op of operations) {
|
|
4646
|
+
if (!op.key || !op.name) continue;
|
|
4647
|
+
manifestKeys.add(op.key);
|
|
4648
|
+
const ex = existingByKey.get(op.key);
|
|
4649
|
+
const endpoint = resolveEndpoint(op.endpoint, operationBaseUrl);
|
|
4650
|
+
if (ex) {
|
|
4651
|
+
await client.operations.updateOperation({
|
|
4652
|
+
id: ex.id,
|
|
4653
|
+
name: op.name,
|
|
4654
|
+
description: op.description,
|
|
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
|
|
4664
|
+
});
|
|
4665
|
+
summary.operations.updated++;
|
|
4666
|
+
} else {
|
|
4667
|
+
await client.operations.createOperation({
|
|
4668
|
+
key: op.key,
|
|
4669
|
+
name: op.name,
|
|
4670
|
+
endpoint,
|
|
4671
|
+
description: op.description,
|
|
4672
|
+
icon: op.icon,
|
|
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,
|
|
4682
|
+
configId
|
|
4683
|
+
});
|
|
4684
|
+
summary.operations.created++;
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
for (const [key, ex] of existingByKey) {
|
|
4688
|
+
if (!manifestKeys.has(key)) {
|
|
4689
|
+
await client.operations.deleteOperation(
|
|
4690
|
+
ex.id
|
|
4691
|
+
);
|
|
4692
|
+
summary.operations.deleted++;
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
async function reconcileHooks(client, configId, hooks, summary) {
|
|
4697
|
+
const existing = await client.hooks.listHooks({ configId, limit: 200 });
|
|
4698
|
+
const existingByKey = new Map(
|
|
4699
|
+
(existing.hooks ?? []).map((h) => [h.key, h])
|
|
4700
|
+
);
|
|
4701
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4702
|
+
for (const hook of hooks) {
|
|
4703
|
+
const key = hook.key || `${hook.event}-${hook.operationKey ?? "default"}`;
|
|
4704
|
+
if (!hook.event) continue;
|
|
4705
|
+
manifestKeys.add(key);
|
|
4706
|
+
const name = hook.name || key;
|
|
4707
|
+
const ex = existingByKey.get(key);
|
|
4708
|
+
if (ex) {
|
|
4709
|
+
await client.hooks.updateHook({
|
|
4710
|
+
id: ex.id,
|
|
4711
|
+
name,
|
|
4712
|
+
description: hook.description,
|
|
4713
|
+
operationKey: hook.operationKey,
|
|
4714
|
+
filter: hook.filter,
|
|
4715
|
+
notificationConfig: hook.notificationConfig,
|
|
4716
|
+
isActive: hook.isActive
|
|
4717
|
+
});
|
|
4718
|
+
summary.hooks.updated++;
|
|
4719
|
+
} else {
|
|
4720
|
+
await client.hooks.createHook({
|
|
4721
|
+
key,
|
|
4722
|
+
name,
|
|
4723
|
+
description: hook.description,
|
|
4724
|
+
event: hook.event,
|
|
4725
|
+
targetType: hook.type ?? "operation",
|
|
4726
|
+
operationKey: hook.operationKey,
|
|
4727
|
+
filter: hook.filter,
|
|
4728
|
+
notificationConfig: hook.notificationConfig,
|
|
4729
|
+
configId
|
|
4730
|
+
});
|
|
4731
|
+
summary.hooks.created++;
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
for (const [key, ex] of existingByKey) {
|
|
4735
|
+
if (!manifestKeys.has(key)) {
|
|
4736
|
+
await client.hooks.deleteHook(
|
|
4737
|
+
ex.id
|
|
4738
|
+
);
|
|
4739
|
+
summary.hooks.deleted++;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
async function reconcileSegments(client, segments, summary) {
|
|
4744
|
+
const existing = await client.segments.listSegments({ limit: 200 });
|
|
4745
|
+
const existingByKey = new Map(
|
|
4746
|
+
(existing.segments ?? []).map((s) => [s.key, s])
|
|
4747
|
+
);
|
|
4748
|
+
for (const seg of segments) {
|
|
4749
|
+
if (!seg.key || !seg.name) continue;
|
|
4750
|
+
const ex = existingByKey.get(seg.key);
|
|
4751
|
+
if (ex) {
|
|
4752
|
+
await client.segments.updateSegment({
|
|
4753
|
+
id: ex.id,
|
|
4754
|
+
name: seg.name,
|
|
4755
|
+
description: seg.description,
|
|
4756
|
+
rules: seg.rules,
|
|
4757
|
+
evaluationMode: seg.evaluationMode,
|
|
4758
|
+
isActive: seg.isActive
|
|
4759
|
+
});
|
|
4760
|
+
summary.segments.updated++;
|
|
4761
|
+
} else {
|
|
4762
|
+
await client.segments.createSegment({
|
|
4763
|
+
key: seg.key,
|
|
4764
|
+
name: seg.name,
|
|
4765
|
+
description: seg.description,
|
|
4766
|
+
rules: seg.rules,
|
|
4767
|
+
evaluationMode: seg.evaluationMode,
|
|
4768
|
+
isActive: seg.isActive
|
|
4769
|
+
});
|
|
4770
|
+
summary.segments.created++;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4774
|
+
async function reconcileCronSchedules(client, configId, schedules, summary) {
|
|
4775
|
+
const existing = await client.cronSchedules.listCronSchedules({ configId, limit: 200 });
|
|
4776
|
+
const schedulesList = existing.schedules ?? [];
|
|
4777
|
+
const existingByKey = new Map(
|
|
4778
|
+
schedulesList.map((s) => [s.key, s])
|
|
4779
|
+
);
|
|
4780
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4781
|
+
for (const sched of schedules) {
|
|
4782
|
+
const key = sched.key ?? sched.operationKey;
|
|
4783
|
+
if (!key || !sched.cron) continue;
|
|
4784
|
+
manifestKeys.add(key);
|
|
4785
|
+
const name = sched.name ?? key;
|
|
4786
|
+
const ex = existingByKey.get(key);
|
|
4787
|
+
if (ex) {
|
|
4788
|
+
await client.cronSchedules.updateCronSchedule({
|
|
4789
|
+
id: ex.id,
|
|
4790
|
+
name,
|
|
4791
|
+
description: sched.description,
|
|
4792
|
+
cron: sched.cron,
|
|
4793
|
+
timezone: sched.timezone,
|
|
4794
|
+
operationKey: sched.operationKey,
|
|
4795
|
+
isActive: sched.enabled
|
|
4796
|
+
});
|
|
4797
|
+
summary.cronSchedules.updated++;
|
|
4798
|
+
} else {
|
|
4799
|
+
await client.cronSchedules.createCronSchedule({
|
|
4800
|
+
key,
|
|
4801
|
+
name,
|
|
4802
|
+
description: sched.description,
|
|
4803
|
+
cron: sched.cron,
|
|
4804
|
+
timezone: sched.timezone,
|
|
4805
|
+
operationKey: sched.operationKey,
|
|
4806
|
+
configId
|
|
4807
|
+
});
|
|
4808
|
+
summary.cronSchedules.created++;
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
for (const [key, ex] of existingByKey) {
|
|
4812
|
+
if (!manifestKeys.has(key)) {
|
|
4813
|
+
await client.cronSchedules.deleteCronSchedule(
|
|
4814
|
+
ex.id
|
|
4815
|
+
);
|
|
4816
|
+
summary.cronSchedules.deleted++;
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
async function reconcileAuthProviders(client, providers, summary) {
|
|
4821
|
+
const existing = await client.identity.listAuthProviders({ limit: 200 });
|
|
4822
|
+
const existingByKey = new Map(
|
|
4823
|
+
existing.items.map((p) => [p.key, p])
|
|
4824
|
+
);
|
|
4825
|
+
for (const prov of providers) {
|
|
4826
|
+
if (!prov.key || !prov.name || !prov.type) continue;
|
|
4827
|
+
const ex = existingByKey.get(prov.key);
|
|
4828
|
+
if (ex) {
|
|
4829
|
+
await client.identity.updateAuthProvider({
|
|
4830
|
+
id: ex.id,
|
|
4831
|
+
name: prov.name,
|
|
4832
|
+
config: prov.config,
|
|
4833
|
+
enabled: prov.enabled,
|
|
4834
|
+
isDefault: prov.isDefault,
|
|
4835
|
+
priority: prov.priority
|
|
4836
|
+
});
|
|
4837
|
+
summary.authProviders.updated++;
|
|
4838
|
+
} else {
|
|
4839
|
+
await client.identity.createAuthProvider({
|
|
4840
|
+
key: prov.key,
|
|
4841
|
+
name: prov.name,
|
|
4842
|
+
type: prov.type.toUpperCase(),
|
|
4843
|
+
config: prov.config,
|
|
4844
|
+
enabled: prov.enabled ?? true,
|
|
4845
|
+
isDefault: prov.isDefault ?? false,
|
|
4846
|
+
priority: prov.priority ?? 0
|
|
4847
|
+
});
|
|
4848
|
+
summary.authProviders.created++;
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
async function reconcileProfileSchema(client, manifest, summary) {
|
|
4853
|
+
const profileSchema = manifest["customerProfileSchema"];
|
|
4854
|
+
if (!profileSchema) return;
|
|
4855
|
+
await client.settings.updateCustomerProfileSchema({
|
|
4856
|
+
fields: profileSchema.fields,
|
|
4857
|
+
publicFields: profileSchema.publicFields ?? []
|
|
4858
|
+
});
|
|
4859
|
+
summary.profileSchemaUpdated = true;
|
|
4860
|
+
}
|
|
4861
|
+
async function reconcileApiKeys(client, apiKeys, summary) {
|
|
4862
|
+
if (apiKeys.length === 0) return;
|
|
4863
|
+
const existing = await client.identity.listApiKeys({ limit: 200 });
|
|
4864
|
+
const existingByName = new Map(
|
|
4865
|
+
existing.items.map((k) => [k.name, k])
|
|
4866
|
+
);
|
|
4867
|
+
for (const key of apiKeys) {
|
|
4868
|
+
if (!key.name || !key.keyType || !key.envVar) continue;
|
|
4869
|
+
if (existingByName.has(key.name)) continue;
|
|
4870
|
+
const result = await client.identity.createApiKey({
|
|
4871
|
+
name: key.name,
|
|
4872
|
+
keyType: key.keyType === "secret" ? 2 : 1,
|
|
4873
|
+
allowedModels: key.allowedModels,
|
|
4874
|
+
allowedFileTypes: key.allowedFileTypes
|
|
4875
|
+
});
|
|
4876
|
+
const rawKey = result?.apiKey?.rawKey;
|
|
4877
|
+
if (rawKey) {
|
|
4878
|
+
summary.apiKeys.push({
|
|
4879
|
+
name: key.name,
|
|
4880
|
+
keyType: key.keyType,
|
|
4881
|
+
envVar: key.envVar,
|
|
4882
|
+
rawKey
|
|
4883
|
+
});
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
// src/commands/push.ts
|
|
4238
4889
|
var CONFIG_FILE_NAMES = [
|
|
4239
4890
|
"foir.config.ts",
|
|
4240
4891
|
"foir.config.js",
|
|
@@ -4265,6 +4916,30 @@ function writeEnvVar(envPath, key, value) {
|
|
|
4265
4916
|
writeFileSync2(envPath, content, "utf-8");
|
|
4266
4917
|
return true;
|
|
4267
4918
|
}
|
|
4919
|
+
function printSummary(summary) {
|
|
4920
|
+
const lines = [];
|
|
4921
|
+
const fmt = (label, c) => {
|
|
4922
|
+
const parts = [];
|
|
4923
|
+
if (c.created) parts.push(`${c.created} created`);
|
|
4924
|
+
if (c.updated) parts.push(`${c.updated} updated`);
|
|
4925
|
+
if (c.deleted) parts.push(`${c.deleted} deleted`);
|
|
4926
|
+
if (parts.length > 0) lines.push(` ${label.padEnd(13)} ${parts.join(", ")}`);
|
|
4927
|
+
};
|
|
4928
|
+
fmt("Models:", summary.models);
|
|
4929
|
+
fmt("Operations:", summary.operations);
|
|
4930
|
+
fmt("Hooks:", summary.hooks);
|
|
4931
|
+
fmt("Segments:", summary.segments);
|
|
4932
|
+
fmt("Schedules:", summary.cronSchedules);
|
|
4933
|
+
fmt("Auth:", summary.authProviders);
|
|
4934
|
+
if (summary.profileSchemaUpdated) {
|
|
4935
|
+
lines.push(" Profile: schema updated");
|
|
4936
|
+
}
|
|
4937
|
+
if (lines.length > 0) {
|
|
4938
|
+
for (const line of lines) {
|
|
4939
|
+
console.log(line);
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4268
4943
|
function registerPushCommand(program2, globalOpts) {
|
|
4269
4944
|
program2.command("push").description("Push foir.config.ts to the platform").option("--config <path>", "Path to config file (default: auto-discover)").option("--force", "Force reinstall (delete and recreate)", false).option("--env <path>", "Path to .env file (default: .env)").action(
|
|
4270
4945
|
withErrorHandler(
|
|
@@ -4286,89 +4961,42 @@ function registerPushCommand(program2, globalOpts) {
|
|
|
4286
4961
|
'Config must have at least "key" and "name" fields.'
|
|
4287
4962
|
);
|
|
4288
4963
|
}
|
|
4289
|
-
if (opts.force) {
|
|
4290
|
-
config2.force = true;
|
|
4291
|
-
}
|
|
4292
4964
|
const client = await createPlatformClient(globalOpts());
|
|
4293
4965
|
console.log(
|
|
4294
4966
|
chalk6.dim(`Pushing config "${config2.key}" to platform...`)
|
|
4295
4967
|
);
|
|
4296
|
-
const
|
|
4968
|
+
const applyResult = await client.configs.applyConfig(
|
|
4297
4969
|
config2.key,
|
|
4298
4970
|
config2
|
|
4299
4971
|
);
|
|
4300
|
-
if (!
|
|
4972
|
+
if (!applyResult) {
|
|
4301
4973
|
throw new Error(
|
|
4302
4974
|
"Failed to apply config \u2014 no response from server."
|
|
4303
4975
|
);
|
|
4304
4976
|
}
|
|
4977
|
+
const configId = applyResult.id;
|
|
4978
|
+
console.log(chalk6.dim("Reconciling resources..."));
|
|
4979
|
+
const summary = await reconcileConfig(client, configId, config2);
|
|
4305
4980
|
console.log();
|
|
4306
4981
|
console.log(chalk6.green("\u2713 Config applied successfully"));
|
|
4307
|
-
console.log(` Config ID: ${chalk6.cyan(
|
|
4308
|
-
console.log(` Config Key: ${chalk6.cyan(
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
console.log();
|
|
4312
|
-
const lines = [];
|
|
4313
|
-
if (summary.modelsCreated || summary.modelsUpdated) {
|
|
4314
|
-
lines.push(
|
|
4315
|
-
` Models: ${summary.modelsCreated ?? 0} created, ${summary.modelsUpdated ?? 0} updated`
|
|
4316
|
-
);
|
|
4317
|
-
}
|
|
4318
|
-
if (summary.operationsCreated || summary.operationsUpdated) {
|
|
4319
|
-
lines.push(
|
|
4320
|
-
` Operations: ${summary.operationsCreated ?? 0} created, ${summary.operationsUpdated ?? 0} updated`
|
|
4321
|
-
);
|
|
4322
|
-
}
|
|
4323
|
-
if (summary.hooksCreated || summary.hooksUpdated) {
|
|
4324
|
-
lines.push(
|
|
4325
|
-
` Hooks: ${summary.hooksCreated ?? 0} created, ${summary.hooksUpdated ?? 0} updated`
|
|
4326
|
-
);
|
|
4327
|
-
}
|
|
4328
|
-
if (summary.segmentsCreated || summary.segmentsUpdated) {
|
|
4329
|
-
lines.push(
|
|
4330
|
-
` Segments: ${summary.segmentsCreated ?? 0} created, ${summary.segmentsUpdated ?? 0} updated`
|
|
4331
|
-
);
|
|
4332
|
-
}
|
|
4333
|
-
if (summary.schedulesCreated || summary.schedulesUpdated) {
|
|
4334
|
-
lines.push(
|
|
4335
|
-
` Schedules: ${summary.schedulesCreated ?? 0} created, ${summary.schedulesUpdated ?? 0} updated`
|
|
4336
|
-
);
|
|
4337
|
-
}
|
|
4338
|
-
if (summary.authProvidersCreated || summary.authProvidersUpdated) {
|
|
4339
|
-
lines.push(
|
|
4340
|
-
` Auth: ${summary.authProvidersCreated ?? 0} created, ${summary.authProvidersUpdated ?? 0} updated`
|
|
4341
|
-
);
|
|
4342
|
-
}
|
|
4343
|
-
if (summary.resourcesDeleted) {
|
|
4344
|
-
lines.push(
|
|
4345
|
-
` Cleaned up: ${summary.resourcesDeleted} orphaned resource(s)`
|
|
4346
|
-
);
|
|
4347
|
-
}
|
|
4348
|
-
if (lines.length > 0) {
|
|
4349
|
-
for (const line of lines) {
|
|
4350
|
-
console.log(line);
|
|
4351
|
-
}
|
|
4352
|
-
}
|
|
4353
|
-
}
|
|
4982
|
+
console.log(` Config ID: ${chalk6.cyan(configId)}`);
|
|
4983
|
+
console.log(` Config Key: ${chalk6.cyan(config2.key)}`);
|
|
4984
|
+
console.log();
|
|
4985
|
+
printSummary(summary);
|
|
4354
4986
|
const envPath = resolve4(opts.env ?? ".env");
|
|
4355
|
-
const r = result;
|
|
4356
|
-
const provisionedKeys = r.provisionedApiKeys;
|
|
4357
|
-
const webhookSecret = r.webhookSecret;
|
|
4358
4987
|
const envWrites = [];
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
});
|
|
4366
|
-
}
|
|
4988
|
+
for (const pk of summary.apiKeys) {
|
|
4989
|
+
envWrites.push({
|
|
4990
|
+
key: pk.envVar,
|
|
4991
|
+
value: pk.rawKey,
|
|
4992
|
+
label: `${pk.name} (${pk.keyType})`
|
|
4993
|
+
});
|
|
4367
4994
|
}
|
|
4368
|
-
|
|
4995
|
+
const { secret: signingSecret } = await client.operations.getSigningSecret();
|
|
4996
|
+
if (signingSecret) {
|
|
4369
4997
|
envWrites.push({
|
|
4370
4998
|
key: "FOIR_WEBHOOK_SECRET",
|
|
4371
|
-
value:
|
|
4999
|
+
value: signingSecret,
|
|
4372
5000
|
label: "Webhook signing secret"
|
|
4373
5001
|
});
|
|
4374
5002
|
}
|
|
@@ -6471,6 +7099,98 @@ function buildDispatchTable() {
|
|
|
6471
7099
|
},
|
|
6472
7100
|
deleteCustomerAuthProvider: async (v, c) => await c.identity.deleteAuthProvider(str(v.id))
|
|
6473
7101
|
},
|
|
7102
|
+
// ── Rollouts ────────────────────────────────────────────────
|
|
7103
|
+
rollouts: {
|
|
7104
|
+
listPublishBatches: async (v, c) => wrapList(
|
|
7105
|
+
await c.records.listPublishBatches({ limit: num(v.limit, 50) })
|
|
7106
|
+
),
|
|
7107
|
+
getPublishBatch: async (v, c) => await c.records.getPublishBatch(str(v.id)),
|
|
7108
|
+
createPublishBatch: async (v, c) => {
|
|
7109
|
+
const input = v.input;
|
|
7110
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7111
|
+
return await c.records.createPublishBatch({
|
|
7112
|
+
name: str(input.name),
|
|
7113
|
+
versionIds: input.versionIds ?? [],
|
|
7114
|
+
scheduledAt: input.scheduledAt ? new Date(String(input.scheduledAt)) : void 0
|
|
7115
|
+
});
|
|
7116
|
+
},
|
|
7117
|
+
updatePublishBatch: async (v, c) => {
|
|
7118
|
+
const input = v.input;
|
|
7119
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7120
|
+
return await c.records.updatePublishBatch({
|
|
7121
|
+
batchId: str(input.id ?? v.id),
|
|
7122
|
+
name: str(input.name),
|
|
7123
|
+
scheduledAt: input.scheduledAt ? new Date(String(input.scheduledAt)) : void 0
|
|
7124
|
+
});
|
|
7125
|
+
},
|
|
7126
|
+
cancelPublishBatch: async (v, c) => await c.records.cancelPublishBatch(str(v.id)),
|
|
7127
|
+
rollbackPublishBatch: async (v, c) => await c.records.rollbackPublishBatch(str(v.id)),
|
|
7128
|
+
retryFailedBatchItems: async (v, c) => await c.records.retryFailedBatchItems(str(v.id)),
|
|
7129
|
+
addItemsToPublishBatch: async (v, c) => {
|
|
7130
|
+
const input = v.input;
|
|
7131
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7132
|
+
return await c.records.addItemsToPublishBatch(
|
|
7133
|
+
str(v.id),
|
|
7134
|
+
input.versionIds ?? []
|
|
7135
|
+
);
|
|
7136
|
+
},
|
|
7137
|
+
removeItemsFromPublishBatch: async (v, c) => {
|
|
7138
|
+
const input = v.input;
|
|
7139
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7140
|
+
return await c.records.removeItemsFromPublishBatch(
|
|
7141
|
+
str(v.id),
|
|
7142
|
+
input.versionIds ?? []
|
|
7143
|
+
);
|
|
7144
|
+
}
|
|
7145
|
+
},
|
|
7146
|
+
// ── Hooks ──────────────────────────────────────────────────
|
|
7147
|
+
hooks: {
|
|
7148
|
+
hooks: async (v, c) => {
|
|
7149
|
+
const resp = await c.hooks.listHooks({ limit: num(v.limit, 50) });
|
|
7150
|
+
return { items: resp.hooks ?? [], total: resp.total ?? 0 };
|
|
7151
|
+
},
|
|
7152
|
+
hookByKey: async (v, c) => await c.hooks.getHookByKey(str(v.key)),
|
|
7153
|
+
createHook: async (v, c) => {
|
|
7154
|
+
const input = v.input;
|
|
7155
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7156
|
+
return await c.hooks.createHook({
|
|
7157
|
+
key: str(input.key),
|
|
7158
|
+
name: str(input.name),
|
|
7159
|
+
event: str(input.event),
|
|
7160
|
+
targetType: str(input.targetType) ?? "operation",
|
|
7161
|
+
description: str(input.description),
|
|
7162
|
+
operationKey: str(input.operationKey),
|
|
7163
|
+
filter: input.filter
|
|
7164
|
+
});
|
|
7165
|
+
},
|
|
7166
|
+
updateHook: async (v, c) => {
|
|
7167
|
+
const input = v.input;
|
|
7168
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7169
|
+
return await c.hooks.updateHook({
|
|
7170
|
+
id: str(input.id ?? v.id),
|
|
7171
|
+
name: str(input.name),
|
|
7172
|
+
operationKey: str(input.operationKey),
|
|
7173
|
+
filter: input.filter,
|
|
7174
|
+
isActive: input.isActive
|
|
7175
|
+
});
|
|
7176
|
+
},
|
|
7177
|
+
deleteHook: async (v, c) => await c.hooks.deleteHook(str(v.id)),
|
|
7178
|
+
hookDeliveries: async (v, c) => {
|
|
7179
|
+
const resp = await c.hooks.listHookDeliveries({
|
|
7180
|
+
hookId: str(v.hookId),
|
|
7181
|
+
limit: num(v.limit, 50)
|
|
7182
|
+
});
|
|
7183
|
+
return { items: resp.deliveries ?? [], total: resp.total ?? 0 };
|
|
7184
|
+
},
|
|
7185
|
+
retryHookDelivery: async (v, c) => await c.hooks.retryHookDelivery(str(v.deliveryId)),
|
|
7186
|
+
testHook: async (v, c) => {
|
|
7187
|
+
const data = v.data;
|
|
7188
|
+
return await c.hooks.testHook({
|
|
7189
|
+
hookId: str(v.hookId),
|
|
7190
|
+
testPayload: data
|
|
7191
|
+
});
|
|
7192
|
+
}
|
|
7193
|
+
},
|
|
6474
7194
|
// ── Configs ─────────────────────────────────────────────────
|
|
6475
7195
|
configs: {
|
|
6476
7196
|
configs: async (v, c) => {
|
|
@@ -23,6 +23,7 @@ interface FieldDefinitionInput {
|
|
|
23
23
|
required?: boolean;
|
|
24
24
|
helpText?: string;
|
|
25
25
|
placeholder?: string;
|
|
26
|
+
defaultValue?: unknown;
|
|
26
27
|
config?: Record<string, unknown>;
|
|
27
28
|
itemType?: string;
|
|
28
29
|
storage?: string;
|
|
@@ -32,6 +33,8 @@ interface FieldDefinitionInput {
|
|
|
32
33
|
interface ApplyConfigModelInput {
|
|
33
34
|
key: string;
|
|
34
35
|
name: string;
|
|
36
|
+
pluralName?: string;
|
|
37
|
+
description?: string;
|
|
35
38
|
fields?: FieldDefinitionInput[];
|
|
36
39
|
config?: Record<string, unknown>;
|
|
37
40
|
}
|
|
@@ -39,11 +42,24 @@ interface ApplyConfigOperationInput {
|
|
|
39
42
|
key: string;
|
|
40
43
|
name: string;
|
|
41
44
|
description?: string;
|
|
45
|
+
icon?: string;
|
|
42
46
|
category?: string;
|
|
43
47
|
/** HTTP endpoint URL that the platform calls when this operation is triggered. */
|
|
44
48
|
endpoint?: string;
|
|
45
|
-
config?: Record<string, unknown>;
|
|
46
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>;
|
|
47
63
|
}
|
|
48
64
|
interface ApplyConfigSegmentInput {
|
|
49
65
|
key: string;
|
|
@@ -54,11 +70,15 @@ interface ApplyConfigSegmentInput {
|
|
|
54
70
|
isActive?: boolean;
|
|
55
71
|
}
|
|
56
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;
|
|
57
78
|
operationKey: string;
|
|
58
79
|
cron: string;
|
|
59
80
|
timezone?: string;
|
|
60
81
|
enabled?: boolean;
|
|
61
|
-
payload?: Record<string, unknown>;
|
|
62
82
|
}
|
|
63
83
|
interface ApplyConfigAuthProviderInput {
|
|
64
84
|
key: string;
|
|
@@ -83,20 +103,20 @@ interface ApplyConfigHookInput {
|
|
|
83
103
|
key?: string;
|
|
84
104
|
/** Display name. */
|
|
85
105
|
name?: string;
|
|
106
|
+
/** Description of what this hook does. */
|
|
107
|
+
description?: string;
|
|
86
108
|
/** Lifecycle event that triggers this hook. */
|
|
87
109
|
event: string;
|
|
110
|
+
/** Target type — defaults to 'operation'. */
|
|
111
|
+
type?: string;
|
|
88
112
|
/** Key of the operation to execute. */
|
|
89
113
|
operationKey?: string;
|
|
90
114
|
/** Filter to scope the hook (e.g., `{ modelKey: 'redirect' }`). */
|
|
91
115
|
filter?: Record<string, unknown>;
|
|
92
|
-
type
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
headers?: Record<string, string>;
|
|
97
|
-
additionalData?: Record<string, unknown>;
|
|
98
|
-
expression?: Record<string, unknown>;
|
|
99
|
-
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;
|
|
100
120
|
}
|
|
101
121
|
interface ApplyConfigApiKeyInput {
|
|
102
122
|
/** Name for this API key (e.g. "Tilly iOS", "Tilly BFF"). */
|
|
@@ -105,14 +125,20 @@ interface ApplyConfigApiKeyInput {
|
|
|
105
125
|
keyType: 'public' | 'secret';
|
|
106
126
|
/** Environment variable name to write the key to in .env (e.g. "FOIR_PUBLIC_KEY"). */
|
|
107
127
|
envVar: string;
|
|
108
|
-
/**
|
|
109
|
-
scopes?:
|
|
128
|
+
/** Scopes to restrict the key (e.g. ["configs:read", "records:write"]). Use ["*"] for full access. */
|
|
129
|
+
scopes?: string[];
|
|
130
|
+
/** Restrict the key to specific model keys (e.g. ["tilly_note", "tilly_block"]). */
|
|
131
|
+
allowedModels?: string[];
|
|
132
|
+
/** Restrict file uploads to specific MIME types (e.g. ["image/*", "video/*"]). */
|
|
133
|
+
allowedFileTypes?: string[];
|
|
110
134
|
}
|
|
111
135
|
interface ApplyConfigInput {
|
|
112
136
|
key: string;
|
|
113
137
|
name: string;
|
|
114
138
|
configType?: string;
|
|
115
139
|
force?: boolean;
|
|
140
|
+
/** Base URL prepended to relative operation endpoints. */
|
|
141
|
+
operationBaseUrl?: string;
|
|
116
142
|
models?: ApplyConfigModelInput[];
|
|
117
143
|
operations?: ApplyConfigOperationInput[];
|
|
118
144
|
segments?: ApplyConfigSegmentInput[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eide/foir-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Universal platform CLI for Foir platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@bufbuild/protobuf": "^2.0.0",
|
|
50
50
|
"@connectrpc/connect": "^2.0.0",
|
|
51
51
|
"@connectrpc/connect-node": "^2.0.0",
|
|
52
|
-
"@eide/foir-proto-ts": "^0.3.
|
|
52
|
+
"@eide/foir-proto-ts": "^0.3.3",
|
|
53
53
|
"chalk": "^5.3.0",
|
|
54
54
|
"commander": "^12.1.0",
|
|
55
55
|
"dotenv": "^16.4.5",
|