@fire-spark/cli 0.0.0-stage → 0.13.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.
Files changed (39) hide show
  1. package/bin/firespark.mjs +14 -0
  2. package/package.json +39 -3
  3. package/src/commands/doctor/client/admin.flow.ts +553 -0
  4. package/src/commands/doctor/client/catalog.json +1586 -0
  5. package/src/commands/doctor/client/catalog.ts +179 -0
  6. package/src/commands/doctor/client/checkout.flow.ts +400 -0
  7. package/src/commands/doctor/client/command.ts +246 -0
  8. package/src/commands/doctor/client/config.ts +346 -0
  9. package/src/commands/doctor/client/content.flow.ts +450 -0
  10. package/src/commands/doctor/client/copy.ts +248 -0
  11. package/src/commands/doctor/client/flow.ts +182 -0
  12. package/src/commands/doctor/client/log.flow.ts +410 -0
  13. package/src/commands/doctor/client/manifests.ts +161 -0
  14. package/src/commands/doctor/client/men.flow.ts +412 -0
  15. package/src/commands/doctor/client/ops.flow.ts +312 -0
  16. package/src/commands/doctor/client/options.ts +201 -0
  17. package/src/commands/doctor/client/ord.flow.ts +445 -0
  18. package/src/commands/doctor/client/order.ts +261 -0
  19. package/src/commands/doctor/client/pay.flow.ts +772 -0
  20. package/src/commands/doctor/client/repo.scan.ts +142 -0
  21. package/src/commands/doctor/client/runner.ts +182 -0
  22. package/src/commands/doctor/client/sdk.flow.ts +630 -0
  23. package/src/commands/doctor/client/store.flow.ts +577 -0
  24. package/src/commands/doctor/client/yaml.ts +179 -0
  25. package/src/commands/doctor/live/http.ts +81 -0
  26. package/src/commands/doctor/live/live.session.ts +250 -0
  27. package/src/commands/doctor/live/order.ts +95 -0
  28. package/src/commands/doctor/live/realtime.ts +161 -0
  29. package/src/commands/doctor/live/result.ts +76 -0
  30. package/src/commands/index.ts +8 -0
  31. package/src/core/command.types.ts +25 -0
  32. package/src/core/help.ts +85 -0
  33. package/src/core/i18n.ts +49 -0
  34. package/src/core/options.ts +131 -0
  35. package/src/core/output.ts +47 -0
  36. package/src/core/redact.ts +71 -0
  37. package/src/index.ts +4 -0
  38. package/src/main.ts +113 -0
  39. package/README.md +0 -3
@@ -0,0 +1,312 @@
1
+ import type { Copy } from "../../../core/i18n";
2
+ import { fromLive, isOutcome, openLive, prepare } from "./flow";
3
+ import { readPage, say, textOf, trpcCall, withSafeFix } from "./flow";
4
+ import { readSources } from "./repo.scan";
5
+ import type { CheckBinding, Outcome, RunCtx } from "./runner";
6
+
7
+ const line = (es: string, en: string, pt: string): Copy => {
8
+ return { "es-EC": es, "en-US": en, "pt-BR": pt };
9
+ };
10
+
11
+ const COPY = {
12
+ channel: line(
13
+ "{id} no aplica al canal {template}.",
14
+ "{id} does not apply to the {template} channel.",
15
+ "{id} não se aplica ao canal {template}."
16
+ ),
17
+ need: line(
18
+ "{id} se omite: falta {field}.",
19
+ "{id} is skipped: {field} is missing.",
20
+ "{id} é omitido: falta {field}."
21
+ ),
22
+ named: line(
23
+ "{id} solo corre cuando lo nombras. No cambié el equipo.",
24
+ "{id} runs only when you name it. The device was not changed.",
25
+ "{id} só roda quando você o nomeia. O equipamento não foi alterado."
26
+ ),
27
+ print: line(
28
+ "El repo del canal no llama a printReceipt.",
29
+ "The channel repo does not call printReceipt.",
30
+ "O repo do canal não chama printReceipt."
31
+ ),
32
+ page: line(
33
+ "La página pública del canal no respondió.",
34
+ "The channel public page did not respond.",
35
+ "A página pública do canal não respondeu."
36
+ ),
37
+ socket: line(
38
+ "RT-10 encontró un listener de realtime fuera de checkout u orden: {file}.",
39
+ "RT-10 found a realtime listener outside checkout or order: {file}.",
40
+ "RT-10 encontrou um listener de realtime fora do checkout ou do pedido: {file}."
41
+ ),
42
+ reboot: line(
43
+ "RT-07 kioskDevices.reboot respondió {status}.",
44
+ "RT-07 kioskDevices.reboot responded {status}.",
45
+ "RT-07 kioskDevices.reboot respondeu {status}."
46
+ )
47
+ };
48
+
49
+ const applies = (ctx: RunCtx, id: string, allowed: ReadonlySet<string>) => {
50
+ const ready = prepare(ctx, ["channel_id"]);
51
+ if (isOutcome(ready)) return ready;
52
+ const template = textOf(ready.template);
53
+ if (allowed.has(template)) return null;
54
+ return say(ctx.locale, "skip", COPY.channel, { id, template });
55
+ };
56
+
57
+ const LIVE = [
58
+ "api_url",
59
+ "channel_id",
60
+ "FIRESPARK_DOCTOR_CUSTOMER_EMAIL",
61
+ "FIRESPARK_DOCTOR_CUSTOMER_PASSWORD",
62
+ "FIRESPARK_DOCTOR_FIREBASE_API_KEY"
63
+ ] as const;
64
+
65
+ const need = (ctx: RunCtx, id: string, field: string) => {
66
+ return say(ctx.locale, "skip", COPY.need, { id, field });
67
+ };
68
+
69
+ const deviceGap = (env: NodeJS.ProcessEnv) => {
70
+ if (!textOf(env.FIRESPARK_DOCTOR_DEVICE_UID)) {
71
+ return "FIRESPARK_DOCTOR_DEVICE_UID";
72
+ }
73
+ if (!textOf(env.FIRESPARK_DOCTOR_DEVICE_SECRET)) {
74
+ return "FIRESPARK_DOCTOR_DEVICE_SECRET";
75
+ }
76
+ return "";
77
+ };
78
+
79
+ const heartbeat = async (ctx: RunCtx, id: string, path: string) => {
80
+ const ready = prepare(ctx, LIVE);
81
+ if (isOutcome(ready)) return ready;
82
+ const field = deviceGap(ctx.env);
83
+ if (field) return need(ctx, id, field);
84
+ const live = openLive(ctx, ready);
85
+ const device_uid = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_UID);
86
+ const device_secret = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_SECRET);
87
+ const version = { app_version_code: 1, app_version_name: "doctor" };
88
+ const body = { device_uid, device_secret, ...version };
89
+ const codes = [200];
90
+ const args = ["device", "POST", path, codes, body] as const;
91
+ const step = await live.probe(...args);
92
+ if (!step.ok) return fromLive(ctx.locale, step.result);
93
+ return { status: "pass" as const };
94
+ };
95
+
96
+ const scoped = (rel: string) => {
97
+ const path = rel.toLowerCase();
98
+ if (path.includes("checkout")) return true;
99
+ if (path.includes("order")) return true;
100
+ return false;
101
+ };
102
+
103
+ const listener = (body: string) => {
104
+ if (body.includes("@fire-spark/realtime")) return true;
105
+ if (body.includes("new WebSocket")) return true;
106
+ return false;
107
+ };
108
+
109
+ const runRt10 = async (ctx: RunCtx): Promise<Outcome> => {
110
+ const allowed = new Set(["web", "app", "chat", "call-center"]);
111
+ const skipped = applies(ctx, "RT-10", allowed);
112
+ if (skipped) return skipped;
113
+ const files = await readSources(ctx.cwd);
114
+ const found = files.find(file => !scoped(file.rel) && listener(file.body));
115
+ if (found) return say(ctx.locale, "fail", COPY.socket, { file: found.rel });
116
+ return { status: "pass" };
117
+ };
118
+
119
+ const runRt06 = async (ctx: RunCtx): Promise<Outcome> => {
120
+ const skipped = applies(ctx, "RT-06", new Set(["kiosk", "dashboard"]));
121
+ if (skipped) return skipped;
122
+ if (!ctx.named.has("RT-06"))
123
+ return say(ctx.locale, "skip", COPY.named, { id: "RT-06" });
124
+ const ready = prepare(ctx, LIVE);
125
+ if (isOutcome(ready)) return ready;
126
+ const field = deviceGap(ctx.env);
127
+ if (field) return need(ctx, "RT-06", field);
128
+ const live = openLive(ctx, ready);
129
+ const device_uid = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_UID);
130
+ const device_secret = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_SECRET);
131
+ const body = { device_uid, device_secret };
132
+ const codes = [200];
133
+ const args = ["unlink", "DELETE", "/kiosk/devices/me", codes, body] as const;
134
+ const step = await live.probe(...args);
135
+ if (!step.ok) return fromLive(ctx.locale, step.result);
136
+ return { status: "pass" };
137
+ };
138
+
139
+ const runRt07 = async (ctx: RunCtx): Promise<Outcome> => {
140
+ const skipped = applies(ctx, "RT-07", new Set(["kiosk", "dashboard"]));
141
+ if (skipped) return skipped;
142
+ const device = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_ID);
143
+ if (!device) return need(ctx, "RT-07", "FIRESPARK_DOCTOR_DEVICE_ID");
144
+ const cookie = textOf(ctx.env.FIRESPARK_DOCTOR_DASHBOARD_COOKIE);
145
+ if (!cookie) return need(ctx, "RT-07", "FIRESPARK_DOCTOR_DASHBOARD_COOKIE");
146
+ const ready = prepare(ctx, ["dashboard_url", "merchant_id", "env"]);
147
+ if (isOutcome(ready)) return ready;
148
+ const url = `${textOf(ready.dashboard_url)}/api/trpc/kioskDevices.reboot`;
149
+ const merchant_id = textOf(ready.merchant_id);
150
+ const env = textOf(ready.env);
151
+ const input = { merchant_id, env, id: device };
152
+ const headers = { "content-type": "application/json", cookie };
153
+ const signal = AbortSignal.timeout(15000);
154
+ const payload = JSON.stringify({ json: input });
155
+ const init = { method: "POST", headers, body: payload, signal };
156
+ try {
157
+ const response = await fetch(url, init);
158
+ if (response.status === 200 || response.status === 412) {
159
+ return { status: "pass" };
160
+ }
161
+ return say(ctx.locale, "fail", COPY.reboot, { status: response.status });
162
+ } catch {
163
+ return say(ctx.locale, "fail", COPY.reboot, { status: 0 });
164
+ }
165
+ };
166
+
167
+ const runRt08 = async (ctx: RunCtx): Promise<Outcome> => {
168
+ const allowed = new Set(["kiosk", "pos", "dashboard"]);
169
+ const skipped = applies(ctx, "RT-08", allowed);
170
+ if (skipped) return skipped;
171
+ const ready = prepare(ctx, ["channel_id"]);
172
+ if (isOutcome(ready)) return ready;
173
+ const pos = textOf(ready.template) === "pos";
174
+ const path = pos ? "/pos/devices/me" : "/kiosk/devices/me";
175
+ return heartbeat(ctx, "RT-08", path);
176
+ };
177
+
178
+ const runRt09 = async (ctx: RunCtx): Promise<Outcome> => {
179
+ const allowed = new Set(["kiosk", "pos", "dashboard"]);
180
+ const skipped = applies(ctx, "RT-09", allowed);
181
+ if (skipped) return skipped;
182
+ const ready = prepare(ctx, ["channel_id"]);
183
+ if (isOutcome(ready)) return ready;
184
+ const template = textOf(ready.template);
185
+ if (template === "dashboard") {
186
+ return say(ctx.locale, "skip", COPY.need, {
187
+ id: "RT-09",
188
+ field: "kiosk channel_id"
189
+ });
190
+ }
191
+ const cookie = textOf(ctx.env.FIRESPARK_DOCTOR_DASHBOARD_COOKIE);
192
+ if (!cookie) {
193
+ return say(ctx.locale, "skip", COPY.need, {
194
+ id: "RT-09",
195
+ field: "FIRESPARK_DOCTOR_DASHBOARD_COOKIE"
196
+ });
197
+ }
198
+ const dash = textOf(ready.dashboard_url);
199
+ if (!dash) {
200
+ return say(ctx.locale, "skip", COPY.need, {
201
+ id: "RT-09",
202
+ field: "dashboard_url"
203
+ });
204
+ }
205
+ const input = {
206
+ merchant_id: textOf(ready.merchant_id),
207
+ env: textOf(ready.env),
208
+ channel_id: textOf(ready.channel_id)
209
+ };
210
+ const proc = "kioskDevices.selectByRange";
211
+ const step = await trpcCall(dash, cookie, proc, input);
212
+ if (step.status < 200 || step.status >= 300) {
213
+ return say(ctx.locale, "fail", COPY.reboot, { status: step.status });
214
+ }
215
+ if (!step.text.includes("store_id")) {
216
+ return say(ctx.locale, "fail", COPY.need, { id: "RT-09", field: "stores" });
217
+ }
218
+ if (step.text.includes("PENDING") && !step.text.includes("LINKED")) {
219
+ return say(ctx.locale, "fail", COPY.need, { id: "RT-09", field: "link" });
220
+ }
221
+ if (!step.text.includes("is_online")) {
222
+ return say(ctx.locale, "fail", COPY.need, { id: "RT-09", field: "online" });
223
+ }
224
+ return { status: "pass" };
225
+ };
226
+
227
+ const runPrt01 = async (ctx: RunCtx): Promise<Outcome> => {
228
+ const skipped = applies(ctx, "PRT-01", new Set(["pos"]));
229
+ if (skipped) return skipped;
230
+ const files = await readSources(ctx.cwd);
231
+ const found = files.some(file => file.body.includes("printReceipt"));
232
+ if (!found) return say(ctx.locale, "fail", COPY.print);
233
+ return { status: "pass" };
234
+ };
235
+
236
+ const currentSession = async (ctx: RunCtx, id: string) => {
237
+ const ready = prepare(ctx, LIVE);
238
+ if (isOutcome(ready)) return ready;
239
+ const field = deviceGap(ctx.env);
240
+ if (field) return need(ctx, id, field);
241
+ const live = openLive(ctx, ready);
242
+ const device_uid = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_UID);
243
+ const device_secret = textOf(ctx.env.FIRESPARK_DOCTOR_DEVICE_SECRET);
244
+ const body = { device_uid, device_secret };
245
+ const codes = [200];
246
+ const p1 = ["session", "POST", "/pos/sessions/current"] as const;
247
+ const args = [...p1, codes, body] as const;
248
+ const step = await live.probe(...args);
249
+ if (!step.ok) return fromLive(ctx.locale, step.result);
250
+ return { status: "pass" as const };
251
+ };
252
+
253
+ const runCaj01 = async (ctx: RunCtx): Promise<Outcome> => {
254
+ const skipped = applies(ctx, "CAJ-01", new Set(["pos", "dashboard"]));
255
+ if (skipped) return skipped;
256
+ return currentSession(ctx, "CAJ-01");
257
+ };
258
+
259
+ const runCaj02 = async (ctx: RunCtx): Promise<Outcome> => {
260
+ const skipped = applies(ctx, "CAJ-02", new Set(["pos"]));
261
+ if (skipped) return skipped;
262
+ return currentSession(ctx, "CAJ-02");
263
+ };
264
+
265
+ const runCaj03 = async (ctx: RunCtx): Promise<Outcome> => {
266
+ const skipped = applies(ctx, "CAJ-03", new Set(["pos"]));
267
+ if (skipped) return skipped;
268
+ return currentSession(ctx, "CAJ-03");
269
+ };
270
+
271
+ const pageOf = async (ctx: RunCtx, id: string) => {
272
+ const ready = prepare(ctx, ["url"]);
273
+ if (isOutcome(ready)) return ready;
274
+ let page: { status: number; body: string };
275
+ try {
276
+ page = await readPage(textOf(ready.url));
277
+ } catch {
278
+ return say(ctx.locale, "fail", COPY.page);
279
+ }
280
+ if (page.status >= 400) return say(ctx.locale, "fail", COPY.page);
281
+ if (!page.body.toLowerCase().includes("whatsapp") && id === "CHT-01") {
282
+ return need(ctx, id, "url");
283
+ }
284
+ return { status: "pass" as const };
285
+ };
286
+
287
+ const runCht01 = async (ctx: RunCtx): Promise<Outcome> => {
288
+ const skipped = applies(ctx, "CHT-01", new Set(["chat", "dashboard"]));
289
+ if (skipped) return skipped;
290
+ return pageOf(ctx, "CHT-01");
291
+ };
292
+
293
+ const runCht03 = async (ctx: RunCtx): Promise<Outcome> => {
294
+ const allowed = new Set(["chat", "call-center", "dashboard"]);
295
+ const skipped = applies(ctx, "CHT-03", allowed);
296
+ if (skipped) return skipped;
297
+ return pageOf(ctx, "CHT-03");
298
+ };
299
+
300
+ export const OPS_BINDINGS: Record<string, CheckBinding> = {
301
+ "RT-06": withSafeFix(runRt06),
302
+ "RT-07": withSafeFix(runRt07),
303
+ "RT-08": withSafeFix(runRt08),
304
+ "RT-09": withSafeFix(runRt09),
305
+ "RT-10": withSafeFix(runRt10),
306
+ "PRT-01": withSafeFix(runPrt01),
307
+ "CAJ-01": withSafeFix(runCaj01),
308
+ "CAJ-02": withSafeFix(runCaj02),
309
+ "CAJ-03": withSafeFix(runCaj03),
310
+ "CHT-01": withSafeFix(runCht01),
311
+ "CHT-03": withSafeFix(runCht03)
312
+ };
@@ -0,0 +1,201 @@
1
+ import type { Copy } from "../../../core/i18n";
2
+ import type { OptionSpec } from "../../../core/options";
3
+
4
+ const text = (es: string, en: string, pt: string): Copy => {
5
+ return { "es-EC": es, "en-US": en, "pt-BR": pt };
6
+ };
7
+
8
+ const option = (spec: OptionSpec) => spec;
9
+
10
+ export const CLIENT_OPTIONS: OptionSpec[] = [
11
+ option({
12
+ name: "config",
13
+ kind: "string",
14
+ env: "FIRESPARK_DOCTOR_CONFIG",
15
+ description: text(
16
+ "YAML de config (por ejemplo .bembos.doctor.yml).",
17
+ "Config YAML (for example .bembos.doctor.yml).",
18
+ "YAML de config (por exemplo .bembos.doctor.yml)."
19
+ )
20
+ }),
21
+ option({
22
+ name: "channel",
23
+ kind: "string",
24
+ description: text(
25
+ "Filtra por canal: kiosk, pos, web, app, chat, call-center, server, dashboard.",
26
+ "Filter by channel: kiosk, pos, web, app, chat, call-center, server, dashboard.",
27
+ "Filtra por canal: kiosk, pos, web, app, chat, call-center, server, dashboard."
28
+ )
29
+ }),
30
+ option({
31
+ name: "platform",
32
+ kind: "string",
33
+ description: text(
34
+ "Filtra por capa: web, android, ios, dashboard.",
35
+ "Filter by platform: web, android, ios, dashboard.",
36
+ "Filtra por plataforma: web, android, ios, dashboard."
37
+ )
38
+ }),
39
+ option({
40
+ name: "required",
41
+ kind: "boolean",
42
+ description: text(
43
+ "Solo checks con Requerido = sí.",
44
+ "Only checks with Required = yes.",
45
+ "Só checks com Obrigatório = sim."
46
+ )
47
+ }),
48
+ option({
49
+ name: "area",
50
+ kind: "string",
51
+ description: text(
52
+ "Filtra por área (Pagos, Órdenes, Menú, SDK y config, …).",
53
+ "Filter by area (Payments, Orders, Menu, SDK and config, …).",
54
+ "Filtra por área (Pagamentos, Pedidos, Cardápio, SDK e config, …)."
55
+ )
56
+ }),
57
+ option({
58
+ name: "verification",
59
+ kind: "string",
60
+ description: text(
61
+ "Filtra por verificación: Determinística o Mezcla.",
62
+ "Filter by verification: Deterministic or Mixed.",
63
+ "Filtra por verificação: Determinística ou Mista."
64
+ )
65
+ }),
66
+ option({
67
+ name: "fix",
68
+ kind: "boolean",
69
+ description: text(
70
+ "Intenta arreglar los checks con Fixable = sí. Sin esta flag, solo informa.",
71
+ "Try to repair checks with Fixable = yes. Without this flag, only report.",
72
+ "Tenta reparar os checks com Fixable = sim. Sem esta flag, só informa."
73
+ )
74
+ }),
75
+ option({
76
+ name: "url",
77
+ kind: "string",
78
+ description: text(
79
+ "URL pública del canal desplegado.",
80
+ "Public URL of the deployed channel.",
81
+ "URL pública do canal implantado."
82
+ )
83
+ }),
84
+ option({
85
+ name: "dashboard-url",
86
+ kind: "string",
87
+ description: text(
88
+ "URL del dashboard desplegado.",
89
+ "URL of the deployed dashboard.",
90
+ "URL do dashboard implantado."
91
+ )
92
+ }),
93
+ option({
94
+ name: "api-url",
95
+ kind: "string",
96
+ env: "FIRESPARK_API_URL",
97
+ description: text(
98
+ "URL de la API del merchant.",
99
+ "Merchant API URL.",
100
+ "URL da API do merchant."
101
+ )
102
+ }),
103
+ option({
104
+ name: "merchant-id",
105
+ kind: "string",
106
+ env: "FIRESPARK_DOCTOR_MERCHANT_ID",
107
+ description: text("ID del merchant.", "Merchant id.", "ID do merchant.")
108
+ }),
109
+ option({
110
+ name: "env",
111
+ kind: "string",
112
+ env: "FIRESPARK_DOCTOR_ENVIRONMENT",
113
+ description: text(
114
+ "ID o nombre del ambiente.",
115
+ "Environment id or name.",
116
+ "ID ou nome do ambiente."
117
+ )
118
+ }),
119
+ option({
120
+ name: "store-id",
121
+ kind: "string",
122
+ env: "FIRESPARK_DOCTOR_STORE_ID",
123
+ description: text("ID de la tienda.", "Store id.", "ID da loja.")
124
+ }),
125
+ option({
126
+ name: "channel-id",
127
+ kind: "string",
128
+ env: "FIRESPARK_DOCTOR_CHANNEL_ID",
129
+ description: text(
130
+ "channel_id del canal en el YAML.",
131
+ "channel_id of the channel in the YAML.",
132
+ "channel_id do canal no YAML."
133
+ )
134
+ }),
135
+ option({
136
+ name: "fulfillment-id",
137
+ kind: "string",
138
+ env: "FIRESPARK_DOCTOR_FULFILLMENT_ID",
139
+ description: text(
140
+ "ID del fulfillment.",
141
+ "Fulfillment id.",
142
+ "ID do fulfillment."
143
+ )
144
+ }),
145
+ option({
146
+ name: "payment",
147
+ kind: "string",
148
+ description: text(
149
+ "Modo de pago: cash o sandbox. Nunca un cobro real.",
150
+ "Payment mode: cash or sandbox. Never a real charge.",
151
+ "Modo de pagamento: cash ou sandbox. Nunca uma cobrança real."
152
+ )
153
+ }),
154
+ option({
155
+ name: "receiver",
156
+ kind: "string",
157
+ description: text(
158
+ "URL del receptor de webhooks.",
159
+ "Webhook receiver URL.",
160
+ "URL do receptor de webhooks."
161
+ )
162
+ }),
163
+ option({
164
+ name: "evidence",
165
+ kind: "string",
166
+ description: text(
167
+ "Ruta local donde se guarda la evidencia.",
168
+ "Local path where evidence is stored.",
169
+ "Caminho local onde a evidência é guardada."
170
+ )
171
+ }),
172
+ option({
173
+ name: "format",
174
+ kind: "string",
175
+ env: "FIRESPARK_DOCTOR_FORMAT",
176
+ defaultValue: "text",
177
+ description: text(
178
+ "Formato de salida: text o json.",
179
+ "Output format: text or json.",
180
+ "Formato de saída: text ou json."
181
+ )
182
+ }),
183
+ option({
184
+ name: "strict",
185
+ kind: "boolean",
186
+ description: text(
187
+ "Falla si algún check se omite.",
188
+ "Fail when any check is skipped.",
189
+ "Falha se algum check for omitido."
190
+ )
191
+ }),
192
+ option({
193
+ name: "deployed",
194
+ kind: "boolean",
195
+ description: text(
196
+ "En init, marca el canal como desplegado.",
197
+ "On init, mark the channel as deployed.",
198
+ "No init, marca o canal como implantado."
199
+ )
200
+ })
201
+ ];