@eide/foir-cli 0.4.1 → 0.4.2
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 +711 -72
- package/dist/lib/config-helpers.d.ts +7 -2
- 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) {
|
|
@@ -2693,6 +2701,264 @@ function createStorageMethods(client) {
|
|
|
2693
2701
|
};
|
|
2694
2702
|
}
|
|
2695
2703
|
|
|
2704
|
+
// src/lib/rpc/operations.ts
|
|
2705
|
+
import { create as create9 } from "@bufbuild/protobuf";
|
|
2706
|
+
import {
|
|
2707
|
+
ListOperationsRequestSchema as ListOperationsRequestSchema2,
|
|
2708
|
+
GetOperationRequestSchema,
|
|
2709
|
+
CreateOperationRequestSchema,
|
|
2710
|
+
UpdateOperationRequestSchema,
|
|
2711
|
+
DeleteOperationRequestSchema
|
|
2712
|
+
} from "@eide/foir-proto-ts/operations/v1/operations_pb";
|
|
2713
|
+
function createOperationsMethods(client) {
|
|
2714
|
+
return {
|
|
2715
|
+
// ── CRUD ──────────────────────────────────────────────────
|
|
2716
|
+
async listOperations(params = {}) {
|
|
2717
|
+
return client.listOperations(
|
|
2718
|
+
create9(ListOperationsRequestSchema2, {
|
|
2719
|
+
configId: params.configId,
|
|
2720
|
+
category: params.category,
|
|
2721
|
+
isActive: params.isActive,
|
|
2722
|
+
search: params.search,
|
|
2723
|
+
limit: params.limit ?? 50,
|
|
2724
|
+
offset: params.offset ?? 0
|
|
2725
|
+
})
|
|
2726
|
+
);
|
|
2727
|
+
},
|
|
2728
|
+
async getOperation(params) {
|
|
2729
|
+
const resp = await client.getOperation(
|
|
2730
|
+
create9(GetOperationRequestSchema, {
|
|
2731
|
+
id: params.id ?? "",
|
|
2732
|
+
key: params.key
|
|
2733
|
+
})
|
|
2734
|
+
);
|
|
2735
|
+
return resp.operation ?? null;
|
|
2736
|
+
},
|
|
2737
|
+
async createOperation(params) {
|
|
2738
|
+
const resp = await client.createOperation(
|
|
2739
|
+
create9(CreateOperationRequestSchema, {
|
|
2740
|
+
key: params.key,
|
|
2741
|
+
name: params.name,
|
|
2742
|
+
endpoint: params.endpoint,
|
|
2743
|
+
description: params.description,
|
|
2744
|
+
icon: params.icon,
|
|
2745
|
+
category: params.category,
|
|
2746
|
+
endpointAuth: params.endpointAuth,
|
|
2747
|
+
timeoutMs: params.timeoutMs,
|
|
2748
|
+
inputSchema: params.inputSchema,
|
|
2749
|
+
outputSchema: params.outputSchema,
|
|
2750
|
+
configId: params.configId
|
|
2751
|
+
})
|
|
2752
|
+
);
|
|
2753
|
+
return resp.operation ?? null;
|
|
2754
|
+
},
|
|
2755
|
+
async updateOperation(params) {
|
|
2756
|
+
const resp = await client.updateOperation(
|
|
2757
|
+
create9(UpdateOperationRequestSchema, {
|
|
2758
|
+
id: params.id,
|
|
2759
|
+
name: params.name,
|
|
2760
|
+
description: params.description,
|
|
2761
|
+
endpoint: params.endpoint,
|
|
2762
|
+
endpointAuth: params.endpointAuth,
|
|
2763
|
+
timeoutMs: params.timeoutMs,
|
|
2764
|
+
inputSchema: params.inputSchema,
|
|
2765
|
+
outputSchema: params.outputSchema,
|
|
2766
|
+
isActive: params.isActive
|
|
2767
|
+
})
|
|
2768
|
+
);
|
|
2769
|
+
return resp.operation ?? null;
|
|
2770
|
+
},
|
|
2771
|
+
async deleteOperation(id) {
|
|
2772
|
+
const resp = await client.deleteOperation(
|
|
2773
|
+
create9(DeleteOperationRequestSchema, { id })
|
|
2774
|
+
);
|
|
2775
|
+
return resp.success;
|
|
2776
|
+
}
|
|
2777
|
+
};
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
// src/lib/rpc/hooks.ts
|
|
2781
|
+
import { create as create10 } from "@bufbuild/protobuf";
|
|
2782
|
+
import {
|
|
2783
|
+
ListHooksRequestSchema,
|
|
2784
|
+
GetHookRequestSchema,
|
|
2785
|
+
GetHookByKeyRequestSchema,
|
|
2786
|
+
CreateHookRequestSchema,
|
|
2787
|
+
UpdateHookRequestSchema,
|
|
2788
|
+
DeleteHookRequestSchema,
|
|
2789
|
+
ListHookDeliveriesRequestSchema,
|
|
2790
|
+
RetryHookDeliveryRequestSchema,
|
|
2791
|
+
TestHookRequestSchema
|
|
2792
|
+
} from "@eide/foir-proto-ts/hooks/v1/hooks_pb";
|
|
2793
|
+
function createHooksMethods(client) {
|
|
2794
|
+
return {
|
|
2795
|
+
// ── Queries ──────────────────────────────────────────────
|
|
2796
|
+
async listHooks(params = {}) {
|
|
2797
|
+
return client.listHooks(
|
|
2798
|
+
create10(ListHooksRequestSchema, {
|
|
2799
|
+
event: params.event,
|
|
2800
|
+
isActive: params.isActive,
|
|
2801
|
+
configId: params.configId,
|
|
2802
|
+
limit: params.limit ?? 50,
|
|
2803
|
+
offset: params.offset ?? 0
|
|
2804
|
+
})
|
|
2805
|
+
);
|
|
2806
|
+
},
|
|
2807
|
+
async getHook(id) {
|
|
2808
|
+
const resp = await client.getHook(create10(GetHookRequestSchema, { id }));
|
|
2809
|
+
return resp.hook ?? null;
|
|
2810
|
+
},
|
|
2811
|
+
// ── Mutations ────────────────────────────────────────────
|
|
2812
|
+
async createHook(params) {
|
|
2813
|
+
const resp = await client.createHook(
|
|
2814
|
+
create10(CreateHookRequestSchema, {
|
|
2815
|
+
key: params.key,
|
|
2816
|
+
name: params.name,
|
|
2817
|
+
event: params.event,
|
|
2818
|
+
targetType: params.targetType,
|
|
2819
|
+
description: params.description,
|
|
2820
|
+
operationKey: params.operationKey,
|
|
2821
|
+
notificationConfig: params.notificationConfig ?? void 0,
|
|
2822
|
+
filter: params.filter ?? void 0,
|
|
2823
|
+
configId: params.configId
|
|
2824
|
+
})
|
|
2825
|
+
);
|
|
2826
|
+
return resp.hook ?? null;
|
|
2827
|
+
},
|
|
2828
|
+
async updateHook(params) {
|
|
2829
|
+
const resp = await client.updateHook(
|
|
2830
|
+
create10(UpdateHookRequestSchema, {
|
|
2831
|
+
id: params.id,
|
|
2832
|
+
name: params.name,
|
|
2833
|
+
description: params.description,
|
|
2834
|
+
operationKey: params.operationKey,
|
|
2835
|
+
notificationConfig: params.notificationConfig ?? void 0,
|
|
2836
|
+
filter: params.filter ?? void 0,
|
|
2837
|
+
isActive: params.isActive
|
|
2838
|
+
})
|
|
2839
|
+
);
|
|
2840
|
+
return resp.hook ?? null;
|
|
2841
|
+
},
|
|
2842
|
+
async deleteHook(id) {
|
|
2843
|
+
const resp = await client.deleteHook(
|
|
2844
|
+
create10(DeleteHookRequestSchema, { id })
|
|
2845
|
+
);
|
|
2846
|
+
return resp.success;
|
|
2847
|
+
},
|
|
2848
|
+
// ── Get by Key ──────────────────────────────────────────
|
|
2849
|
+
async getHookByKey(key) {
|
|
2850
|
+
const resp = await client.getHookByKey(
|
|
2851
|
+
create10(GetHookByKeyRequestSchema, { key })
|
|
2852
|
+
);
|
|
2853
|
+
return resp.hook ?? null;
|
|
2854
|
+
},
|
|
2855
|
+
// ── Deliveries ──────────────────────────────────────────
|
|
2856
|
+
async listHookDeliveries(params) {
|
|
2857
|
+
return client.listHookDeliveries(
|
|
2858
|
+
create10(ListHookDeliveriesRequestSchema, {
|
|
2859
|
+
hookId: params.hookId,
|
|
2860
|
+
status: params.status,
|
|
2861
|
+
limit: params.limit ?? 50,
|
|
2862
|
+
offset: params.offset ?? 0
|
|
2863
|
+
})
|
|
2864
|
+
);
|
|
2865
|
+
},
|
|
2866
|
+
async retryHookDelivery(deliveryId) {
|
|
2867
|
+
const resp = await client.retryHookDelivery(
|
|
2868
|
+
create10(RetryHookDeliveryRequestSchema, { deliveryId })
|
|
2869
|
+
);
|
|
2870
|
+
return resp.success;
|
|
2871
|
+
},
|
|
2872
|
+
// ── Testing ─────────────────────────────────────────────
|
|
2873
|
+
async testHook(params) {
|
|
2874
|
+
const resp = await client.testHook(
|
|
2875
|
+
create10(TestHookRequestSchema, {
|
|
2876
|
+
hookId: params.hookId,
|
|
2877
|
+
testPayload: params.testPayload
|
|
2878
|
+
})
|
|
2879
|
+
);
|
|
2880
|
+
return {
|
|
2881
|
+
success: resp.success,
|
|
2882
|
+
error: resp.error ?? void 0
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
// src/lib/rpc/cron-schedules.ts
|
|
2889
|
+
import { create as create11 } from "@bufbuild/protobuf";
|
|
2890
|
+
import {
|
|
2891
|
+
ListCronSchedulesRequestSchema,
|
|
2892
|
+
GetCronScheduleRequestSchema,
|
|
2893
|
+
GetCronScheduleByKeyRequestSchema,
|
|
2894
|
+
CreateCronScheduleRequestSchema,
|
|
2895
|
+
UpdateCronScheduleRequestSchema,
|
|
2896
|
+
DeleteCronScheduleRequestSchema
|
|
2897
|
+
} from "@eide/foir-proto-ts/schedules/v1/schedules_pb";
|
|
2898
|
+
function createCronSchedulesMethods(client) {
|
|
2899
|
+
return {
|
|
2900
|
+
// ── Queries ──────────────────────────────────────────────
|
|
2901
|
+
async listCronSchedules(params = {}) {
|
|
2902
|
+
return client.listCronSchedules(
|
|
2903
|
+
create11(ListCronSchedulesRequestSchema, {
|
|
2904
|
+
configId: params.configId,
|
|
2905
|
+
isActive: params.isActive,
|
|
2906
|
+
limit: params.limit ?? 50,
|
|
2907
|
+
offset: params.offset ?? 0
|
|
2908
|
+
})
|
|
2909
|
+
);
|
|
2910
|
+
},
|
|
2911
|
+
async getCronSchedule(id) {
|
|
2912
|
+
const resp = await client.getCronSchedule(
|
|
2913
|
+
create11(GetCronScheduleRequestSchema, { id })
|
|
2914
|
+
);
|
|
2915
|
+
return resp.schedule ?? null;
|
|
2916
|
+
},
|
|
2917
|
+
async getCronScheduleByKey(key) {
|
|
2918
|
+
const resp = await client.getCronScheduleByKey(
|
|
2919
|
+
create11(GetCronScheduleByKeyRequestSchema, { key })
|
|
2920
|
+
);
|
|
2921
|
+
return resp.schedule ?? null;
|
|
2922
|
+
},
|
|
2923
|
+
// ── Mutations ────────────────────────────────────────────
|
|
2924
|
+
async createCronSchedule(params) {
|
|
2925
|
+
const resp = await client.createCronSchedule(
|
|
2926
|
+
create11(CreateCronScheduleRequestSchema, {
|
|
2927
|
+
key: params.key,
|
|
2928
|
+
name: params.name,
|
|
2929
|
+
cron: params.cron,
|
|
2930
|
+
timezone: params.timezone,
|
|
2931
|
+
operationKey: params.operationKey,
|
|
2932
|
+
configId: params.configId,
|
|
2933
|
+
targetType: params.targetType,
|
|
2934
|
+
targetConfig: params.targetConfig
|
|
2935
|
+
})
|
|
2936
|
+
);
|
|
2937
|
+
return resp.schedule ?? null;
|
|
2938
|
+
},
|
|
2939
|
+
async updateCronSchedule(params) {
|
|
2940
|
+
const resp = await client.updateCronSchedule(
|
|
2941
|
+
create11(UpdateCronScheduleRequestSchema, {
|
|
2942
|
+
id: params.id,
|
|
2943
|
+
name: params.name,
|
|
2944
|
+
description: params.description,
|
|
2945
|
+
cron: params.cron,
|
|
2946
|
+
timezone: params.timezone,
|
|
2947
|
+
operationKey: params.operationKey,
|
|
2948
|
+
isActive: params.isActive
|
|
2949
|
+
})
|
|
2950
|
+
);
|
|
2951
|
+
return resp.schedule ?? null;
|
|
2952
|
+
},
|
|
2953
|
+
async deleteCronSchedule(id) {
|
|
2954
|
+
const resp = await client.deleteCronSchedule(
|
|
2955
|
+
create11(DeleteCronScheduleRequestSchema, { id })
|
|
2956
|
+
);
|
|
2957
|
+
return resp.success;
|
|
2958
|
+
}
|
|
2959
|
+
};
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2696
2962
|
// src/lib/client.ts
|
|
2697
2963
|
import { GraphQLClient } from "graphql-request";
|
|
2698
2964
|
async function createPlatformClient(options) {
|
|
@@ -2739,7 +3005,14 @@ async function createPlatformClient(options) {
|
|
|
2739
3005
|
createRpcClient(ExperimentsService2, transport)
|
|
2740
3006
|
),
|
|
2741
3007
|
settings: createSettingsMethods(createRpcClient(SettingsService2, transport)),
|
|
2742
|
-
storage: createStorageMethods(createRpcClient(StorageService2, transport))
|
|
3008
|
+
storage: createStorageMethods(createRpcClient(StorageService2, transport)),
|
|
3009
|
+
operations: createOperationsMethods(
|
|
3010
|
+
createRpcClient(OperationsService2, transport)
|
|
3011
|
+
),
|
|
3012
|
+
hooks: createHooksMethods(createRpcClient(HooksService2, transport)),
|
|
3013
|
+
cronSchedules: createCronSchedulesMethods(
|
|
3014
|
+
createRpcClient(SchedulesService2, transport)
|
|
3015
|
+
)
|
|
2743
3016
|
};
|
|
2744
3017
|
}
|
|
2745
3018
|
function createPlatformClientWithHeaders(apiUrl, headers) {
|
|
@@ -2764,7 +3037,14 @@ function createPlatformClientWithHeaders(apiUrl, headers) {
|
|
|
2764
3037
|
createRpcClient(ExperimentsService2, transport)
|
|
2765
3038
|
),
|
|
2766
3039
|
settings: createSettingsMethods(createRpcClient(SettingsService2, transport)),
|
|
2767
|
-
storage: createStorageMethods(createRpcClient(StorageService2, transport))
|
|
3040
|
+
storage: createStorageMethods(createRpcClient(StorageService2, transport)),
|
|
3041
|
+
operations: createOperationsMethods(
|
|
3042
|
+
createRpcClient(OperationsService2, transport)
|
|
3043
|
+
),
|
|
3044
|
+
hooks: createHooksMethods(createRpcClient(HooksService2, transport)),
|
|
3045
|
+
cronSchedules: createCronSchedulesMethods(
|
|
3046
|
+
createRpcClient(SchedulesService2, transport)
|
|
3047
|
+
)
|
|
2768
3048
|
};
|
|
2769
3049
|
}
|
|
2770
3050
|
async function getStorageAuth(options) {
|
|
@@ -4235,6 +4515,296 @@ Edit the files, then run:
|
|
|
4235
4515
|
import chalk6 from "chalk";
|
|
4236
4516
|
import { existsSync as existsSync4, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4237
4517
|
import { resolve as resolve4 } from "path";
|
|
4518
|
+
|
|
4519
|
+
// src/lib/reconciler.ts
|
|
4520
|
+
function zeroCounts() {
|
|
4521
|
+
return { created: 0, updated: 0, deleted: 0 };
|
|
4522
|
+
}
|
|
4523
|
+
async function reconcileConfig(client, configId, manifest) {
|
|
4524
|
+
const summary = {
|
|
4525
|
+
models: zeroCounts(),
|
|
4526
|
+
operations: zeroCounts(),
|
|
4527
|
+
hooks: zeroCounts(),
|
|
4528
|
+
segments: zeroCounts(),
|
|
4529
|
+
cronSchedules: zeroCounts(),
|
|
4530
|
+
authProviders: zeroCounts(),
|
|
4531
|
+
profileSchemaUpdated: false,
|
|
4532
|
+
apiKeys: []
|
|
4533
|
+
};
|
|
4534
|
+
await reconcileModels(client, configId, manifest.models ?? [], summary);
|
|
4535
|
+
await reconcileOperations(client, configId, manifest.operations ?? [], summary);
|
|
4536
|
+
await reconcileHooks(client, configId, manifest.hooks ?? [], summary);
|
|
4537
|
+
await reconcileSegments(client, manifest.segments ?? [], summary);
|
|
4538
|
+
await reconcileCronSchedules(client, configId, manifest.schedules ?? [], summary);
|
|
4539
|
+
await reconcileAuthProviders(client, manifest.authProviders ?? [], summary);
|
|
4540
|
+
await reconcileProfileSchema(client, manifest, summary);
|
|
4541
|
+
await reconcileApiKeys(client, manifest.apiKeys ?? [], summary);
|
|
4542
|
+
return summary;
|
|
4543
|
+
}
|
|
4544
|
+
async function reconcileModels(client, configId, models, summary) {
|
|
4545
|
+
const existing = await client.models.listModels({ limit: 200 });
|
|
4546
|
+
const configOwned = existing.items.filter(
|
|
4547
|
+
(m) => m.configId === configId
|
|
4548
|
+
);
|
|
4549
|
+
const existingByKey = new Map(
|
|
4550
|
+
configOwned.map((m) => [m.key, m])
|
|
4551
|
+
);
|
|
4552
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4553
|
+
for (const m of models) {
|
|
4554
|
+
if (!m.key || !m.name) continue;
|
|
4555
|
+
manifestKeys.add(m.key);
|
|
4556
|
+
const ex = existingByKey.get(m.key);
|
|
4557
|
+
if (ex) {
|
|
4558
|
+
await client.models.updateModel({
|
|
4559
|
+
id: ex.id,
|
|
4560
|
+
name: m.name,
|
|
4561
|
+
fields: m.fields,
|
|
4562
|
+
config: m.config
|
|
4563
|
+
});
|
|
4564
|
+
summary.models.updated++;
|
|
4565
|
+
} else {
|
|
4566
|
+
await client.models.createModel({
|
|
4567
|
+
key: m.key,
|
|
4568
|
+
name: m.name,
|
|
4569
|
+
fields: m.fields,
|
|
4570
|
+
config: m.config,
|
|
4571
|
+
configId
|
|
4572
|
+
});
|
|
4573
|
+
summary.models.created++;
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
for (const [key, ex] of existingByKey) {
|
|
4577
|
+
if (!manifestKeys.has(key)) {
|
|
4578
|
+
await client.models.deleteModel(
|
|
4579
|
+
ex.id
|
|
4580
|
+
);
|
|
4581
|
+
summary.models.deleted++;
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
async function reconcileOperations(client, configId, operations, summary) {
|
|
4586
|
+
const existing = await client.operations.listOperations({ configId, limit: 200 });
|
|
4587
|
+
const existingByKey = new Map(
|
|
4588
|
+
(existing.operations ?? []).map((o) => [o.key, o])
|
|
4589
|
+
);
|
|
4590
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4591
|
+
for (const op of operations) {
|
|
4592
|
+
if (!op.key || !op.name) continue;
|
|
4593
|
+
manifestKeys.add(op.key);
|
|
4594
|
+
const ex = existingByKey.get(op.key);
|
|
4595
|
+
if (ex) {
|
|
4596
|
+
await client.operations.updateOperation({
|
|
4597
|
+
id: ex.id,
|
|
4598
|
+
name: op.name,
|
|
4599
|
+
description: op.description,
|
|
4600
|
+
endpoint: op.endpoint
|
|
4601
|
+
});
|
|
4602
|
+
summary.operations.updated++;
|
|
4603
|
+
} else {
|
|
4604
|
+
await client.operations.createOperation({
|
|
4605
|
+
key: op.key,
|
|
4606
|
+
name: op.name,
|
|
4607
|
+
endpoint: op.endpoint ?? "",
|
|
4608
|
+
description: op.description,
|
|
4609
|
+
category: op.category,
|
|
4610
|
+
configId
|
|
4611
|
+
});
|
|
4612
|
+
summary.operations.created++;
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
for (const [key, ex] of existingByKey) {
|
|
4616
|
+
if (!manifestKeys.has(key)) {
|
|
4617
|
+
await client.operations.deleteOperation(
|
|
4618
|
+
ex.id
|
|
4619
|
+
);
|
|
4620
|
+
summary.operations.deleted++;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
async function reconcileHooks(client, configId, hooks, summary) {
|
|
4625
|
+
const existing = await client.hooks.listHooks({ configId, limit: 200 });
|
|
4626
|
+
const existingByKey = new Map(
|
|
4627
|
+
(existing.hooks ?? []).map((h) => [h.key, h])
|
|
4628
|
+
);
|
|
4629
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4630
|
+
for (const hook of hooks) {
|
|
4631
|
+
const key = hook.key || `${hook.event}-${hook.operationKey ?? "default"}`;
|
|
4632
|
+
if (!hook.event) continue;
|
|
4633
|
+
manifestKeys.add(key);
|
|
4634
|
+
const name = hook.name || key;
|
|
4635
|
+
const ex = existingByKey.get(key);
|
|
4636
|
+
if (ex) {
|
|
4637
|
+
await client.hooks.updateHook({
|
|
4638
|
+
id: ex.id,
|
|
4639
|
+
name,
|
|
4640
|
+
operationKey: hook.operationKey,
|
|
4641
|
+
filter: hook.filter
|
|
4642
|
+
});
|
|
4643
|
+
summary.hooks.updated++;
|
|
4644
|
+
} else {
|
|
4645
|
+
await client.hooks.createHook({
|
|
4646
|
+
key,
|
|
4647
|
+
name,
|
|
4648
|
+
event: hook.event,
|
|
4649
|
+
targetType: hook.type ?? "operation",
|
|
4650
|
+
operationKey: hook.operationKey,
|
|
4651
|
+
filter: hook.filter,
|
|
4652
|
+
configId
|
|
4653
|
+
});
|
|
4654
|
+
summary.hooks.created++;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
for (const [key, ex] of existingByKey) {
|
|
4658
|
+
if (!manifestKeys.has(key)) {
|
|
4659
|
+
await client.hooks.deleteHook(
|
|
4660
|
+
ex.id
|
|
4661
|
+
);
|
|
4662
|
+
summary.hooks.deleted++;
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
async function reconcileSegments(client, segments, summary) {
|
|
4667
|
+
const existing = await client.segments.listSegments({ limit: 200 });
|
|
4668
|
+
const existingByKey = new Map(
|
|
4669
|
+
(existing.segments ?? []).map((s) => [s.key, s])
|
|
4670
|
+
);
|
|
4671
|
+
for (const seg of segments) {
|
|
4672
|
+
if (!seg.key || !seg.name) continue;
|
|
4673
|
+
const ex = existingByKey.get(seg.key);
|
|
4674
|
+
if (ex) {
|
|
4675
|
+
await client.segments.updateSegment({
|
|
4676
|
+
id: ex.id,
|
|
4677
|
+
name: seg.name,
|
|
4678
|
+
description: seg.description,
|
|
4679
|
+
rules: seg.rules,
|
|
4680
|
+
evaluationMode: seg.evaluationMode,
|
|
4681
|
+
isActive: seg.isActive
|
|
4682
|
+
});
|
|
4683
|
+
summary.segments.updated++;
|
|
4684
|
+
} else {
|
|
4685
|
+
await client.segments.createSegment({
|
|
4686
|
+
key: seg.key,
|
|
4687
|
+
name: seg.name,
|
|
4688
|
+
description: seg.description,
|
|
4689
|
+
rules: seg.rules,
|
|
4690
|
+
evaluationMode: seg.evaluationMode,
|
|
4691
|
+
isActive: seg.isActive
|
|
4692
|
+
});
|
|
4693
|
+
summary.segments.created++;
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
async function reconcileCronSchedules(client, configId, schedules, summary) {
|
|
4698
|
+
const existing = await client.cronSchedules.listCronSchedules({ configId, limit: 200 });
|
|
4699
|
+
const schedulesList = existing.schedules ?? [];
|
|
4700
|
+
const existingByKey = new Map(
|
|
4701
|
+
schedulesList.map((s) => [s.key, s])
|
|
4702
|
+
);
|
|
4703
|
+
const manifestKeys = /* @__PURE__ */ new Set();
|
|
4704
|
+
for (const sched of schedules) {
|
|
4705
|
+
const key = sched.operationKey;
|
|
4706
|
+
if (!key || !sched.cron) continue;
|
|
4707
|
+
manifestKeys.add(key);
|
|
4708
|
+
const ex = existingByKey.get(key);
|
|
4709
|
+
if (ex) {
|
|
4710
|
+
await client.cronSchedules.updateCronSchedule({
|
|
4711
|
+
id: ex.id,
|
|
4712
|
+
name: key,
|
|
4713
|
+
cron: sched.cron,
|
|
4714
|
+
timezone: sched.timezone,
|
|
4715
|
+
operationKey: sched.operationKey
|
|
4716
|
+
});
|
|
4717
|
+
summary.cronSchedules.updated++;
|
|
4718
|
+
} else {
|
|
4719
|
+
await client.cronSchedules.createCronSchedule({
|
|
4720
|
+
key,
|
|
4721
|
+
name: key,
|
|
4722
|
+
cron: sched.cron,
|
|
4723
|
+
timezone: sched.timezone,
|
|
4724
|
+
operationKey: sched.operationKey,
|
|
4725
|
+
configId
|
|
4726
|
+
});
|
|
4727
|
+
summary.cronSchedules.created++;
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
for (const [key, ex] of existingByKey) {
|
|
4731
|
+
if (!manifestKeys.has(key)) {
|
|
4732
|
+
await client.cronSchedules.deleteCronSchedule(
|
|
4733
|
+
ex.id
|
|
4734
|
+
);
|
|
4735
|
+
summary.cronSchedules.deleted++;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4739
|
+
async function reconcileAuthProviders(client, providers, summary) {
|
|
4740
|
+
const existing = await client.identity.listAuthProviders({ limit: 200 });
|
|
4741
|
+
const existingByKey = new Map(
|
|
4742
|
+
existing.items.map((p) => [p.key, p])
|
|
4743
|
+
);
|
|
4744
|
+
for (const prov of providers) {
|
|
4745
|
+
if (!prov.key || !prov.name || !prov.type) continue;
|
|
4746
|
+
const ex = existingByKey.get(prov.key);
|
|
4747
|
+
if (ex) {
|
|
4748
|
+
await client.identity.updateAuthProvider({
|
|
4749
|
+
id: ex.id,
|
|
4750
|
+
name: prov.name,
|
|
4751
|
+
config: prov.config,
|
|
4752
|
+
enabled: prov.enabled,
|
|
4753
|
+
isDefault: prov.isDefault,
|
|
4754
|
+
priority: prov.priority
|
|
4755
|
+
});
|
|
4756
|
+
summary.authProviders.updated++;
|
|
4757
|
+
} else {
|
|
4758
|
+
await client.identity.createAuthProvider({
|
|
4759
|
+
key: prov.key,
|
|
4760
|
+
name: prov.name,
|
|
4761
|
+
type: prov.type.toUpperCase(),
|
|
4762
|
+
config: prov.config,
|
|
4763
|
+
enabled: prov.enabled ?? true,
|
|
4764
|
+
isDefault: prov.isDefault ?? false,
|
|
4765
|
+
priority: prov.priority ?? 0
|
|
4766
|
+
});
|
|
4767
|
+
summary.authProviders.created++;
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
async function reconcileProfileSchema(client, manifest, summary) {
|
|
4772
|
+
const profileSchema = manifest["customerProfileSchema"];
|
|
4773
|
+
if (!profileSchema) return;
|
|
4774
|
+
await client.settings.updateCustomerProfileSchema({
|
|
4775
|
+
fields: profileSchema.fields,
|
|
4776
|
+
publicFields: profileSchema.publicFields ?? []
|
|
4777
|
+
});
|
|
4778
|
+
summary.profileSchemaUpdated = true;
|
|
4779
|
+
}
|
|
4780
|
+
async function reconcileApiKeys(client, apiKeys, summary) {
|
|
4781
|
+
if (apiKeys.length === 0) return;
|
|
4782
|
+
const existing = await client.identity.listApiKeys({ limit: 200 });
|
|
4783
|
+
const existingByName = new Map(
|
|
4784
|
+
existing.items.map((k) => [k.name, k])
|
|
4785
|
+
);
|
|
4786
|
+
for (const key of apiKeys) {
|
|
4787
|
+
if (!key.name || !key.keyType || !key.envVar) continue;
|
|
4788
|
+
if (existingByName.has(key.name)) continue;
|
|
4789
|
+
const result = await client.identity.createApiKey({
|
|
4790
|
+
name: key.name,
|
|
4791
|
+
keyType: key.keyType === "secret" ? 2 : 1,
|
|
4792
|
+
allowedModels: key.allowedModels,
|
|
4793
|
+
allowedFileTypes: key.allowedFileTypes
|
|
4794
|
+
});
|
|
4795
|
+
const rawKey = result?.apiKey?.rawKey;
|
|
4796
|
+
if (rawKey) {
|
|
4797
|
+
summary.apiKeys.push({
|
|
4798
|
+
name: key.name,
|
|
4799
|
+
keyType: key.keyType,
|
|
4800
|
+
envVar: key.envVar,
|
|
4801
|
+
rawKey
|
|
4802
|
+
});
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
|
|
4807
|
+
// src/commands/push.ts
|
|
4238
4808
|
var CONFIG_FILE_NAMES = [
|
|
4239
4809
|
"foir.config.ts",
|
|
4240
4810
|
"foir.config.js",
|
|
@@ -4265,6 +4835,30 @@ function writeEnvVar(envPath, key, value) {
|
|
|
4265
4835
|
writeFileSync2(envPath, content, "utf-8");
|
|
4266
4836
|
return true;
|
|
4267
4837
|
}
|
|
4838
|
+
function printSummary(summary) {
|
|
4839
|
+
const lines = [];
|
|
4840
|
+
const fmt = (label, c) => {
|
|
4841
|
+
const parts = [];
|
|
4842
|
+
if (c.created) parts.push(`${c.created} created`);
|
|
4843
|
+
if (c.updated) parts.push(`${c.updated} updated`);
|
|
4844
|
+
if (c.deleted) parts.push(`${c.deleted} deleted`);
|
|
4845
|
+
if (parts.length > 0) lines.push(` ${label.padEnd(13)} ${parts.join(", ")}`);
|
|
4846
|
+
};
|
|
4847
|
+
fmt("Models:", summary.models);
|
|
4848
|
+
fmt("Operations:", summary.operations);
|
|
4849
|
+
fmt("Hooks:", summary.hooks);
|
|
4850
|
+
fmt("Segments:", summary.segments);
|
|
4851
|
+
fmt("Schedules:", summary.cronSchedules);
|
|
4852
|
+
fmt("Auth:", summary.authProviders);
|
|
4853
|
+
if (summary.profileSchemaUpdated) {
|
|
4854
|
+
lines.push(" Profile: schema updated");
|
|
4855
|
+
}
|
|
4856
|
+
if (lines.length > 0) {
|
|
4857
|
+
for (const line of lines) {
|
|
4858
|
+
console.log(line);
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4268
4862
|
function registerPushCommand(program2, globalOpts) {
|
|
4269
4863
|
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
4864
|
withErrorHandler(
|
|
@@ -4286,85 +4880,38 @@ function registerPushCommand(program2, globalOpts) {
|
|
|
4286
4880
|
'Config must have at least "key" and "name" fields.'
|
|
4287
4881
|
);
|
|
4288
4882
|
}
|
|
4289
|
-
if (opts.force) {
|
|
4290
|
-
config2.force = true;
|
|
4291
|
-
}
|
|
4292
4883
|
const client = await createPlatformClient(globalOpts());
|
|
4293
4884
|
console.log(
|
|
4294
4885
|
chalk6.dim(`Pushing config "${config2.key}" to platform...`)
|
|
4295
4886
|
);
|
|
4296
|
-
const
|
|
4887
|
+
const applyResult = await client.configs.applyConfig(
|
|
4297
4888
|
config2.key,
|
|
4298
4889
|
config2
|
|
4299
4890
|
);
|
|
4300
|
-
if (!
|
|
4891
|
+
if (!applyResult) {
|
|
4301
4892
|
throw new Error(
|
|
4302
4893
|
"Failed to apply config \u2014 no response from server."
|
|
4303
4894
|
);
|
|
4304
4895
|
}
|
|
4896
|
+
const configId = applyResult.id;
|
|
4897
|
+
console.log(chalk6.dim("Reconciling resources..."));
|
|
4898
|
+
const summary = await reconcileConfig(client, configId, config2);
|
|
4305
4899
|
console.log();
|
|
4306
4900
|
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
|
-
}
|
|
4901
|
+
console.log(` Config ID: ${chalk6.cyan(configId)}`);
|
|
4902
|
+
console.log(` Config Key: ${chalk6.cyan(config2.key)}`);
|
|
4903
|
+
console.log();
|
|
4904
|
+
printSummary(summary);
|
|
4354
4905
|
const envPath = resolve4(opts.env ?? ".env");
|
|
4355
|
-
const r = result;
|
|
4356
|
-
const provisionedKeys = r.provisionedApiKeys;
|
|
4357
|
-
const webhookSecret = r.webhookSecret;
|
|
4358
4906
|
const envWrites = [];
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
});
|
|
4366
|
-
}
|
|
4907
|
+
for (const pk of summary.apiKeys) {
|
|
4908
|
+
envWrites.push({
|
|
4909
|
+
key: pk.envVar,
|
|
4910
|
+
value: pk.rawKey,
|
|
4911
|
+
label: `${pk.name} (${pk.keyType})`
|
|
4912
|
+
});
|
|
4367
4913
|
}
|
|
4914
|
+
const webhookSecret = applyResult.webhookSecret;
|
|
4368
4915
|
if (webhookSecret) {
|
|
4369
4916
|
envWrites.push({
|
|
4370
4917
|
key: "FOIR_WEBHOOK_SECRET",
|
|
@@ -6471,6 +7018,98 @@ function buildDispatchTable() {
|
|
|
6471
7018
|
},
|
|
6472
7019
|
deleteCustomerAuthProvider: async (v, c) => await c.identity.deleteAuthProvider(str(v.id))
|
|
6473
7020
|
},
|
|
7021
|
+
// ── Rollouts ────────────────────────────────────────────────
|
|
7022
|
+
rollouts: {
|
|
7023
|
+
listPublishBatches: async (v, c) => wrapList(
|
|
7024
|
+
await c.records.listPublishBatches({ limit: num(v.limit, 50) })
|
|
7025
|
+
),
|
|
7026
|
+
getPublishBatch: async (v, c) => await c.records.getPublishBatch(str(v.id)),
|
|
7027
|
+
createPublishBatch: async (v, c) => {
|
|
7028
|
+
const input = v.input;
|
|
7029
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7030
|
+
return await c.records.createPublishBatch({
|
|
7031
|
+
name: str(input.name),
|
|
7032
|
+
versionIds: input.versionIds ?? [],
|
|
7033
|
+
scheduledAt: input.scheduledAt ? new Date(String(input.scheduledAt)) : void 0
|
|
7034
|
+
});
|
|
7035
|
+
},
|
|
7036
|
+
updatePublishBatch: async (v, c) => {
|
|
7037
|
+
const input = v.input;
|
|
7038
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7039
|
+
return await c.records.updatePublishBatch({
|
|
7040
|
+
batchId: str(input.id ?? v.id),
|
|
7041
|
+
name: str(input.name),
|
|
7042
|
+
scheduledAt: input.scheduledAt ? new Date(String(input.scheduledAt)) : void 0
|
|
7043
|
+
});
|
|
7044
|
+
},
|
|
7045
|
+
cancelPublishBatch: async (v, c) => await c.records.cancelPublishBatch(str(v.id)),
|
|
7046
|
+
rollbackPublishBatch: async (v, c) => await c.records.rollbackPublishBatch(str(v.id)),
|
|
7047
|
+
retryFailedBatchItems: async (v, c) => await c.records.retryFailedBatchItems(str(v.id)),
|
|
7048
|
+
addItemsToPublishBatch: async (v, c) => {
|
|
7049
|
+
const input = v.input;
|
|
7050
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7051
|
+
return await c.records.addItemsToPublishBatch(
|
|
7052
|
+
str(v.id),
|
|
7053
|
+
input.versionIds ?? []
|
|
7054
|
+
);
|
|
7055
|
+
},
|
|
7056
|
+
removeItemsFromPublishBatch: async (v, c) => {
|
|
7057
|
+
const input = v.input;
|
|
7058
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7059
|
+
return await c.records.removeItemsFromPublishBatch(
|
|
7060
|
+
str(v.id),
|
|
7061
|
+
input.versionIds ?? []
|
|
7062
|
+
);
|
|
7063
|
+
}
|
|
7064
|
+
},
|
|
7065
|
+
// ── Hooks ──────────────────────────────────────────────────
|
|
7066
|
+
hooks: {
|
|
7067
|
+
hooks: async (v, c) => {
|
|
7068
|
+
const resp = await c.hooks.listHooks({ limit: num(v.limit, 50) });
|
|
7069
|
+
return { items: resp.hooks ?? [], total: resp.total ?? 0 };
|
|
7070
|
+
},
|
|
7071
|
+
hookByKey: async (v, c) => await c.hooks.getHookByKey(str(v.key)),
|
|
7072
|
+
createHook: async (v, c) => {
|
|
7073
|
+
const input = v.input;
|
|
7074
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7075
|
+
return await c.hooks.createHook({
|
|
7076
|
+
key: str(input.key),
|
|
7077
|
+
name: str(input.name),
|
|
7078
|
+
event: str(input.event),
|
|
7079
|
+
targetType: str(input.targetType) ?? "operation",
|
|
7080
|
+
description: str(input.description),
|
|
7081
|
+
operationKey: str(input.operationKey),
|
|
7082
|
+
filter: input.filter
|
|
7083
|
+
});
|
|
7084
|
+
},
|
|
7085
|
+
updateHook: async (v, c) => {
|
|
7086
|
+
const input = v.input;
|
|
7087
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
7088
|
+
return await c.hooks.updateHook({
|
|
7089
|
+
id: str(input.id ?? v.id),
|
|
7090
|
+
name: str(input.name),
|
|
7091
|
+
operationKey: str(input.operationKey),
|
|
7092
|
+
filter: input.filter,
|
|
7093
|
+
isActive: input.isActive
|
|
7094
|
+
});
|
|
7095
|
+
},
|
|
7096
|
+
deleteHook: async (v, c) => await c.hooks.deleteHook(str(v.id)),
|
|
7097
|
+
hookDeliveries: async (v, c) => {
|
|
7098
|
+
const resp = await c.hooks.listHookDeliveries({
|
|
7099
|
+
hookId: str(v.hookId),
|
|
7100
|
+
limit: num(v.limit, 50)
|
|
7101
|
+
});
|
|
7102
|
+
return { items: resp.deliveries ?? [], total: resp.total ?? 0 };
|
|
7103
|
+
},
|
|
7104
|
+
retryHookDelivery: async (v, c) => await c.hooks.retryHookDelivery(str(v.deliveryId)),
|
|
7105
|
+
testHook: async (v, c) => {
|
|
7106
|
+
const data = v.data;
|
|
7107
|
+
return await c.hooks.testHook({
|
|
7108
|
+
hookId: str(v.hookId),
|
|
7109
|
+
testPayload: data
|
|
7110
|
+
});
|
|
7111
|
+
}
|
|
7112
|
+
},
|
|
6474
7113
|
// ── Configs ─────────────────────────────────────────────────
|
|
6475
7114
|
configs: {
|
|
6476
7115
|
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;
|
|
@@ -105,8 +106,12 @@ interface ApplyConfigApiKeyInput {
|
|
|
105
106
|
keyType: 'public' | 'secret';
|
|
106
107
|
/** Environment variable name to write the key to in .env (e.g. "FOIR_PUBLIC_KEY"). */
|
|
107
108
|
envVar: string;
|
|
108
|
-
/**
|
|
109
|
-
scopes?:
|
|
109
|
+
/** Scopes to restrict the key (e.g. ["configs:read", "records:write"]). Use ["*"] for full access. */
|
|
110
|
+
scopes?: string[];
|
|
111
|
+
/** Restrict the key to specific model keys (e.g. ["tilly_note", "tilly_block"]). */
|
|
112
|
+
allowedModels?: string[];
|
|
113
|
+
/** Restrict file uploads to specific MIME types (e.g. ["image/*", "video/*"]). */
|
|
114
|
+
allowedFileTypes?: string[];
|
|
110
115
|
}
|
|
111
116
|
interface ApplyConfigInput {
|
|
112
117
|
key: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eide/foir-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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",
|