@cat-factory/app 0.84.0 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/board/RecurringPipelineModal.vue +16 -2
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +23 -3
- package/app/composables/api/recurring.ts +8 -3
- package/app/stores/recurringPipelines.ts +13 -5
- package/i18n/locales/en.json +6 -2
- package/i18n/locales/es.json +6 -2
- package/i18n/locales/fr.json +6 -2
- package/i18n/locales/he.json +6 -2
- package/i18n/locales/ja.json +6 -2
- package/i18n/locales/pl.json +6 -2
- package/i18n/locales/tr.json +6 -2
- package/i18n/locales/uk.json +6 -2
- package/package.json +2 -2
|
@@ -32,6 +32,9 @@ const description = ref('')
|
|
|
32
32
|
const pipelineId = ref('')
|
|
33
33
|
const saving = ref(false)
|
|
34
34
|
const recurrence = ref<Recurrence>(defaultRecurrence())
|
|
35
|
+
// On-demand: no cadence, fires only via "run now". Because a person is present at fire time,
|
|
36
|
+
// its block may use an individual-usage subscription model (which a cadence schedule can't).
|
|
37
|
+
const onDemand = ref(false)
|
|
35
38
|
|
|
36
39
|
// Tracker config (only relevant when the tech-debt pipeline is picked).
|
|
37
40
|
const trackerKind = ref<'github' | 'jira' | 'linear' | null>(null)
|
|
@@ -83,6 +86,7 @@ watch(open, (isOpen) => {
|
|
|
83
86
|
pipelines.pipelines[0]?.id ??
|
|
84
87
|
''
|
|
85
88
|
recurrence.value = defaultRecurrence()
|
|
89
|
+
onDemand.value = false
|
|
86
90
|
saving.value = false
|
|
87
91
|
trackerKind.value = tracker.settings.tracker
|
|
88
92
|
jiraProjectKey.value = tracker.settings.jiraProjectKey ?? ''
|
|
@@ -110,7 +114,9 @@ async function add() {
|
|
|
110
114
|
pipelineId: pipelineId.value,
|
|
111
115
|
template: template.value,
|
|
112
116
|
name: name.value.trim(),
|
|
113
|
-
|
|
117
|
+
// An on-demand schedule carries no cadence; a scheduled one sends its recurrence.
|
|
118
|
+
onDemand: onDemand.value,
|
|
119
|
+
...(onDemand.value ? {} : { recurrence: recurrence.value }),
|
|
114
120
|
...(description.value.trim() ? { description: description.value.trim() } : {}),
|
|
115
121
|
})
|
|
116
122
|
ui.closeAddRecurring()
|
|
@@ -173,7 +179,15 @@ async function add() {
|
|
|
173
179
|
/>
|
|
174
180
|
</UFormField>
|
|
175
181
|
|
|
176
|
-
<
|
|
182
|
+
<div class="flex items-start gap-2 rounded-lg border border-slate-800 p-3">
|
|
183
|
+
<USwitch v-model="onDemand" size="sm" class="mt-0.5" />
|
|
184
|
+
<div class="space-y-0.5">
|
|
185
|
+
<p class="text-xs font-medium text-slate-200">{{ t('board.recurring.onDemand') }}</p>
|
|
186
|
+
<p class="text-[11px] text-slate-500">{{ t('board.recurring.onDemandHint') }}</p>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<RecurringRecurrenceEditor v-if="!onDemand" v-model="recurrence" />
|
|
177
191
|
|
|
178
192
|
<div v-if="isTechDebt" class="space-y-3 rounded-lg border border-slate-800 p-3">
|
|
179
193
|
<p class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
@@ -150,8 +150,18 @@ function fmtTime(ms: number) {
|
|
|
150
150
|
<UIcon name="i-lucide-repeat" class="h-3.5 w-3.5" />
|
|
151
151
|
{{ t('inspector.recurring.title') }}
|
|
152
152
|
</span>
|
|
153
|
-
<UBadge
|
|
154
|
-
|
|
153
|
+
<UBadge
|
|
154
|
+
:color="schedule.onDemand ? 'primary' : schedule.enabled ? 'primary' : 'neutral'"
|
|
155
|
+
variant="subtle"
|
|
156
|
+
size="xs"
|
|
157
|
+
>
|
|
158
|
+
{{
|
|
159
|
+
schedule.onDemand
|
|
160
|
+
? t('inspector.recurring.onDemand')
|
|
161
|
+
: schedule.enabled
|
|
162
|
+
? t('inspector.recurring.active')
|
|
163
|
+
: t('inspector.recurring.paused')
|
|
164
|
+
}}
|
|
155
165
|
</UBadge>
|
|
156
166
|
</div>
|
|
157
167
|
|
|
@@ -159,7 +169,17 @@ function fmtTime(ms: number) {
|
|
|
159
169
|
<span class="text-slate-300">{{ pipelineName }}</span>
|
|
160
170
|
</p>
|
|
161
171
|
|
|
162
|
-
|
|
172
|
+
<!-- On-demand: no cadence, no pause/resume — it fires only when a person triggers it. -->
|
|
173
|
+
<template v-if="schedule.onDemand">
|
|
174
|
+
<p class="text-[11px] text-slate-500">{{ t('inspector.recurring.onDemandHint') }}</p>
|
|
175
|
+
<div class="flex flex-wrap gap-1.5 pt-1">
|
|
176
|
+
<UButton size="xs" variant="soft" icon="i-lucide-play" :loading="busy" @click="runNow">
|
|
177
|
+
{{ t('inspector.recurring.runNow') }}
|
|
178
|
+
</UButton>
|
|
179
|
+
</div>
|
|
180
|
+
</template>
|
|
181
|
+
|
|
182
|
+
<template v-else-if="!editing">
|
|
163
183
|
<p class="text-[11px] text-slate-400">{{ describeCadence(schedule.recurrence) }}</p>
|
|
164
184
|
<p class="text-[11px] text-slate-500">
|
|
165
185
|
{{ t('inspector.recurring.nextRun', { time: fmtTime(schedule.nextRunAt) }) }}
|
|
@@ -21,7 +21,7 @@ import type { ApiContext, Position } from './context'
|
|
|
21
21
|
type CreateScheduleBody = NonNullable<SendParams<typeof createScheduleContract>['body']>
|
|
22
22
|
|
|
23
23
|
/** Recurring (scheduled) pipelines + the in-org shared-service mount catalog. */
|
|
24
|
-
export function recurringApi({ send, ws }: ApiContext) {
|
|
24
|
+
export function recurringApi({ send, sendWith, ws, pwHeaders }: ApiContext) {
|
|
25
25
|
return {
|
|
26
26
|
// ---- recurring pipelines (scheduled runs against a service) -----------
|
|
27
27
|
listRecurringPipelines: (workspaceId: string) =>
|
|
@@ -46,8 +46,13 @@ export function recurringApi({ send, ws }: ApiContext) {
|
|
|
46
46
|
pathParams: { scheduleId: id },
|
|
47
47
|
}),
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// On-demand schedules may target an individual-usage model, so run-now carries the
|
|
50
|
+
// initiator's personal password (like a manual start) — prompted + retried on a 428.
|
|
51
|
+
runScheduleNow: (workspaceId: string, id: string, password?: string) =>
|
|
52
|
+
sendWith(pwHeaders(password), runScheduleNowContract, {
|
|
53
|
+
pathPrefix: ws(workspaceId),
|
|
54
|
+
pathParams: { scheduleId: id },
|
|
55
|
+
}),
|
|
51
56
|
|
|
52
57
|
// ---- in-org shared services (mount/unmount + org catalog) -------------
|
|
53
58
|
// The services this workspace mounts, and the org catalog it can mount from. A 503
|
|
@@ -80,12 +80,20 @@ export const useRecurringPipelinesStore = defineStore('recurringPipelines', () =
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Fire a schedule now. An on-demand schedule may target an individual-usage model, so the
|
|
85
|
+
* initiator's personal password is supplied transparently from the cache and prompted via
|
|
86
|
+
* the credential modal (then retried) when the server replies 428. Returns false when the
|
|
87
|
+
* user cancels the password prompt; true once the fire is accepted.
|
|
88
|
+
*/
|
|
89
|
+
async function runNow(id: string): Promise<boolean> {
|
|
84
90
|
const ws = useWorkspaceStore()
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
const personal = usePersonalSubscriptionsStore()
|
|
92
|
+
return personal.withCredential(async (password) => {
|
|
93
|
+
await api.runScheduleNow(ws.requireId(), id, password)
|
|
94
|
+
await loadRuns(id)
|
|
95
|
+
await ws.refresh()
|
|
96
|
+
})
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/** Fetch (and cache) a schedule's run history for the inspector. */
|
package/i18n/locales/en.json
CHANGED
|
@@ -217,7 +217,9 @@
|
|
|
217
217
|
"linearTeamId": "Linear team id",
|
|
218
218
|
"footerHint": "A single recurring task is added inside the service; each run replaces the last. Its run history is visible in the inspector.",
|
|
219
219
|
"submit": "Add recurring pipeline",
|
|
220
|
-
"addFailedTitle": "Could not add recurring pipeline"
|
|
220
|
+
"addFailedTitle": "Could not add recurring pipeline",
|
|
221
|
+
"onDemand": "On-demand (manual only)",
|
|
222
|
+
"onDemandHint": "Runs only when you trigger it, with no schedule. Because you are present each time, its task may use an individual-usage subscription model."
|
|
221
223
|
},
|
|
222
224
|
"failure": {
|
|
223
225
|
"containerFailedToStart": "Container failed to start",
|
|
@@ -481,7 +483,9 @@
|
|
|
481
483
|
"skipped": "Skipped"
|
|
482
484
|
},
|
|
483
485
|
"updateFailed": "Could not update schedule",
|
|
484
|
-
"runNowFailed": "Could not run now"
|
|
486
|
+
"runNowFailed": "Could not run now",
|
|
487
|
+
"onDemand": "On-demand",
|
|
488
|
+
"onDemandHint": "Manual only. Runs when you trigger it, and may use an individual-usage subscription model."
|
|
485
489
|
},
|
|
486
490
|
"fragments": {
|
|
487
491
|
"serviceTitle": "Service best practices",
|
package/i18n/locales/es.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "ID del equipo de Linear",
|
|
197
197
|
"footerHint": "Se añade una única tarea recurrente dentro del servicio; cada ejecución reemplaza a la anterior. Su historial de ejecuciones es visible en el inspector.",
|
|
198
198
|
"submit": "Añadir pipeline recurrente",
|
|
199
|
-
"addFailedTitle": "No se pudo añadir la pipeline recurrente"
|
|
199
|
+
"addFailedTitle": "No se pudo añadir la pipeline recurrente",
|
|
200
|
+
"onDemand": "Bajo demanda (solo manual)",
|
|
201
|
+
"onDemandHint": "Se ejecuta solo cuando lo activas, sin programación. Como estás presente cada vez, su tarea puede usar un modelo de suscripción de uso individual."
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "El contenedor no pudo iniciarse",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "Omitida"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "No se pudo actualizar la programación",
|
|
441
|
-
"runNowFailed": "No se pudo ejecutar ahora"
|
|
443
|
+
"runNowFailed": "No se pudo ejecutar ahora",
|
|
444
|
+
"onDemand": "Bajo demanda",
|
|
445
|
+
"onDemandHint": "Solo manual. Se ejecuta cuando lo activas y puede usar un modelo de suscripción de uso individual."
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "Buenas prácticas del servicio",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "ID d’équipe Linear",
|
|
197
197
|
"footerHint": "Une seule tâche récurrente est ajoutée dans le service ; chaque exécution remplace la précédente. Son historique d’exécutions est visible dans l’inspecteur.",
|
|
198
198
|
"submit": "Ajouter la pipeline récurrente",
|
|
199
|
-
"addFailedTitle": "Impossible d’ajouter la pipeline récurrente"
|
|
199
|
+
"addFailedTitle": "Impossible d’ajouter la pipeline récurrente",
|
|
200
|
+
"onDemand": "À la demande (manuel uniquement)",
|
|
201
|
+
"onDemandHint": "Ne s'exécute que lorsque vous le déclenchez, sans planification. Comme vous êtes présent à chaque fois, sa tâche peut utiliser un modèle d'abonnement à usage individuel."
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "Le conteneur n’a pas pu démarrer",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "Ignoré"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "Impossible de mettre à jour la planification",
|
|
441
|
-
"runNowFailed": "Impossible d'exécuter maintenant"
|
|
443
|
+
"runNowFailed": "Impossible d'exécuter maintenant",
|
|
444
|
+
"onDemand": "À la demande",
|
|
445
|
+
"onDemandHint": "Manuel uniquement. S'exécute lorsque vous le déclenchez et peut utiliser un modèle d'abonnement à usage individuel."
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "Bonnes pratiques du service",
|
package/i18n/locales/he.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "מזהה צוות Linear",
|
|
197
197
|
"footerHint": "משימה מחזורית אחת נוספת בתוך השירות; כל ריצה מחליפה את הקודמת. היסטוריית הריצות שלה מוצגת במפקח.",
|
|
198
198
|
"submit": "הוסף צינור מחזורי",
|
|
199
|
-
"addFailedTitle": "לא ניתן היה להוסיף צינור מחזורי"
|
|
199
|
+
"addFailedTitle": "לא ניתן היה להוסיף צינור מחזורי",
|
|
200
|
+
"onDemand": "לפי דרישה (ידני בלבד)",
|
|
201
|
+
"onDemandHint": "רץ רק כשאתה מפעיל אותו, ללא תזמון. מכיוון שאתה נוכח בכל פעם, המשימה יכולה להשתמש במודל מנוי לשימוש אישי."
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "מכל הקונטיינר נכשל בהפעלה",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "דולג"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "לא ניתן לעדכן את לוח הזמנים",
|
|
441
|
-
"runNowFailed": "לא ניתן להריץ עכשיו"
|
|
443
|
+
"runNowFailed": "לא ניתן להריץ עכשיו",
|
|
444
|
+
"onDemand": "לפי דרישה",
|
|
445
|
+
"onDemandHint": "ידני בלבד. רץ כשאתה מפעיל אותו, ויכול להשתמש במודל מנוי לשימוש אישי."
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "מומלצות עבודה לשירות",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "Linear チーム ID",
|
|
197
197
|
"footerHint": "サービス内に繰り返しタスクが 1 つ追加され、各実行が前回を置き換えます。実行履歴はインスペクターで確認できます。",
|
|
198
198
|
"submit": "繰り返しパイプラインを追加",
|
|
199
|
-
"addFailedTitle": "繰り返しパイプラインを追加できませんでした"
|
|
199
|
+
"addFailedTitle": "繰り返しパイプラインを追加できませんでした",
|
|
200
|
+
"onDemand": "オンデマンド(手動のみ)",
|
|
201
|
+
"onDemandHint": "スケジュールはなく、手動で実行したときのみ動作します。毎回ユーザーが立ち会うため、タスクは個人利用のサブスクリプションモデルを使用できます。"
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "コンテナの起動に失敗しました",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "スキップ"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "スケジュールを更新できませんでした",
|
|
441
|
-
"runNowFailed": "今すぐ実行できませんでした"
|
|
443
|
+
"runNowFailed": "今すぐ実行できませんでした",
|
|
444
|
+
"onDemand": "オンデマンド",
|
|
445
|
+
"onDemandHint": "手動のみ。実行したときに動作し、個人利用のサブスクリプションモデルを使用できます。"
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "サービスのベストプラクティス",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "ID zespołu Linear",
|
|
197
197
|
"footerHint": "W usłudze dodawane jest jedno cykliczne zadanie; każde uruchomienie zastępuje poprzednie. Jego historia uruchomień jest widoczna w inspektorze.",
|
|
198
198
|
"submit": "Dodaj cykliczny pipeline",
|
|
199
|
-
"addFailedTitle": "Nie udało się dodać cyklicznego pipeline’u"
|
|
199
|
+
"addFailedTitle": "Nie udało się dodać cyklicznego pipeline’u",
|
|
200
|
+
"onDemand": "Na żądanie (tylko ręcznie)",
|
|
201
|
+
"onDemandHint": "Uruchamia się tylko po ręcznym wyzwoleniu, bez harmonogramu. Ponieważ za każdym razem jesteś obecny, jego zadanie może korzystać z modelu subskrypcji do użytku indywidualnego."
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "Nie udało się uruchomić kontenera",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "Pominięto"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "Nie udało się zaktualizować harmonogramu",
|
|
441
|
-
"runNowFailed": "Nie udało się uruchomić teraz"
|
|
443
|
+
"runNowFailed": "Nie udało się uruchomić teraz",
|
|
444
|
+
"onDemand": "Na żądanie",
|
|
445
|
+
"onDemandHint": "Tylko ręcznie. Uruchamia się po wyzwoleniu i może korzystać z modelu subskrypcji do użytku indywidualnego."
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "Dobre praktyki usługi",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "Linear takım kimliği",
|
|
197
197
|
"footerHint": "Servisin içine tek bir yinelenen görev eklenir; her çalıştırma bir öncekinin yerini alır. Çalıştırma geçmişi inceleme panelinde görünür.",
|
|
198
198
|
"submit": "Yinelenen pipeline ekle",
|
|
199
|
-
"addFailedTitle": "Yinelenen pipeline eklenemedi"
|
|
199
|
+
"addFailedTitle": "Yinelenen pipeline eklenemedi",
|
|
200
|
+
"onDemand": "İstek üzerine (yalnızca manuel)",
|
|
201
|
+
"onDemandHint": "Yalnızca siz tetiklediğinizde çalışır, zamanlama yoktur. Her seferinde siz hazır bulunduğunuz için görevi bireysel kullanımlı bir abonelik modeli kullanabilir."
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "Konteyner başlatılamadı",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "Atlandı"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "Zamanlama güncellenemedi",
|
|
441
|
-
"runNowFailed": "Şimdi çalıştırılamadı"
|
|
443
|
+
"runNowFailed": "Şimdi çalıştırılamadı",
|
|
444
|
+
"onDemand": "İstek üzerine",
|
|
445
|
+
"onDemandHint": "Yalnızca manuel. Siz tetiklediğinizde çalışır ve bireysel kullanımlı bir abonelik modeli kullanabilir."
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "Servis en iyi uygulamaları",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -196,7 +196,9 @@
|
|
|
196
196
|
"linearTeamId": "ID команди Linear",
|
|
197
197
|
"footerHint": "У сервісі додається одне періодичне завдання; кожен запуск замінює попередній. Його історія запусків доступна в інспекторі.",
|
|
198
198
|
"submit": "Додати періодичний конвеєр",
|
|
199
|
-
"addFailedTitle": "Не вдалося додати періодичний конвеєр"
|
|
199
|
+
"addFailedTitle": "Не вдалося додати періодичний конвеєр",
|
|
200
|
+
"onDemand": "За запитом (лише вручну)",
|
|
201
|
+
"onDemandHint": "Запускається лише коли ви його активуєте, без розкладу. Оскільки ви присутні щоразу, його завдання може використовувати модель підписки для індивідуального використання."
|
|
200
202
|
},
|
|
201
203
|
"failure": {
|
|
202
204
|
"containerFailedToStart": "Не вдалося запустити контейнер",
|
|
@@ -438,7 +440,9 @@
|
|
|
438
440
|
"skipped": "Пропущено"
|
|
439
441
|
},
|
|
440
442
|
"updateFailed": "Не вдалося оновити розклад",
|
|
441
|
-
"runNowFailed": "Не вдалося запустити зараз"
|
|
443
|
+
"runNowFailed": "Не вдалося запустити зараз",
|
|
444
|
+
"onDemand": "За запитом",
|
|
445
|
+
"onDemandHint": "Лише вручну. Запускається коли ви активуєте, і може використовувати модель підписки для індивідуального використання."
|
|
442
446
|
},
|
|
443
447
|
"fragments": {
|
|
444
448
|
"serviceTitle": "Найкращі практики сервісу",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"valibot": "^1.4.2",
|
|
35
35
|
"vue": "^3.5.39",
|
|
36
36
|
"wretch": "^3.0.9",
|
|
37
|
-
"@cat-factory/contracts": "0.
|
|
37
|
+
"@cat-factory/contracts": "0.92.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@toad-contracts/testing": "0.3.2",
|