zinkee 0.1.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.
Files changed (61) hide show
  1. package/AGENTS.md +46 -0
  2. package/README.md +395 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +7142 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +32 -0
  7. package/src/api/automations.ts +403 -0
  8. package/src/api/comments.ts +51 -0
  9. package/src/api/displays.ts +276 -0
  10. package/src/api/files.ts +50 -0
  11. package/src/api/navigation.ts +71 -0
  12. package/src/api/records.ts +167 -0
  13. package/src/api/schemas.ts +130 -0
  14. package/src/api/teamspace.ts +110 -0
  15. package/src/cli-examples.ts +175 -0
  16. package/src/cli-runner.ts +73 -0
  17. package/src/client.test.ts +128 -0
  18. package/src/client.ts +243 -0
  19. package/src/command-registry.ts +749 -0
  20. package/src/commands/automations.test.ts +728 -0
  21. package/src/commands/automations.ts +2013 -0
  22. package/src/commands/comments.test.ts +214 -0
  23. package/src/commands/comments.ts +303 -0
  24. package/src/commands/config.test.ts +81 -0
  25. package/src/commands/config.ts +150 -0
  26. package/src/commands/displays.test.ts +266 -0
  27. package/src/commands/displays.ts +755 -0
  28. package/src/commands/files.test.ts +284 -0
  29. package/src/commands/files.ts +280 -0
  30. package/src/commands/navigation.test.ts +214 -0
  31. package/src/commands/navigation.ts +348 -0
  32. package/src/commands/profiles.test.ts +82 -0
  33. package/src/commands/profiles.ts +281 -0
  34. package/src/commands/records.test.ts +367 -0
  35. package/src/commands/records.ts +726 -0
  36. package/src/commands/schemas.test.ts +748 -0
  37. package/src/commands/schemas.ts +746 -0
  38. package/src/commands/teamspace.test.ts +229 -0
  39. package/src/commands/teamspace.ts +540 -0
  40. package/src/config.test.ts +165 -0
  41. package/src/config.ts +248 -0
  42. package/src/index.test.ts +140 -0
  43. package/src/index.ts +8 -0
  44. package/src/parsers/expressions.test.ts +50 -0
  45. package/src/parsers/expressions.ts +111 -0
  46. package/src/parsers/kv.test.ts +35 -0
  47. package/src/parsers/kv.ts +49 -0
  48. package/src/parsers/selectors.test.ts +23 -0
  49. package/src/parsers/selectors.ts +29 -0
  50. package/src/program.ts +56 -0
  51. package/src/runtime-context.ts +18 -0
  52. package/src/types.ts +71 -0
  53. package/src/utils/errors.test.ts +48 -0
  54. package/src/utils/errors.ts +183 -0
  55. package/src/utils/examples.test.ts +67 -0
  56. package/src/utils/examples.ts +855 -0
  57. package/src/utils/output.test.ts +39 -0
  58. package/src/utils/output.ts +124 -0
  59. package/src/utils/schema-fields.ts +529 -0
  60. package/tsconfig.json +20 -0
  61. package/tsup.config.ts +13 -0
@@ -0,0 +1,855 @@
1
+ import { AutomationsApi } from "../api/automations.js";
2
+ import { SchemasApi, type ApiSchema, type ApiSchemaField } from "../api/schemas.js";
3
+ import { ApiClient } from "../client.js";
4
+ import { defaultConfigPath, readConfig } from "../config.js";
5
+ import type { NormalizedUuidOrSlug } from "../parsers/selectors.js";
6
+ import { resolveRuntimeContext } from "../runtime-context.js";
7
+ import type { RuntimeOverrides } from "../types.js";
8
+ import {
9
+ getSchemaFieldTypeDefinitions,
10
+ resolveSchemaFieldType,
11
+ } from "./schema-fields.js";
12
+
13
+ export interface CommandExample {
14
+ command: string;
15
+ description: string;
16
+ raw?: string;
17
+ }
18
+
19
+ export interface ExampleContextOptions {
20
+ enabled?: boolean;
21
+ configPath?: string;
22
+ overrides?: RuntimeOverrides;
23
+ }
24
+
25
+ export function buildCommandExample(example: CommandExample): CommandExample {
26
+ return {
27
+ command: example.command,
28
+ description: example.description,
29
+ raw: example.raw,
30
+ };
31
+ }
32
+
33
+ export function buildCommandExamples(examples: readonly CommandExample[]): readonly CommandExample[] {
34
+ return examples.map((example) => buildCommandExample(example));
35
+ }
36
+
37
+ const SAMPLE_UUID = "550e8400-e29b-41d4-a716-446655440001";
38
+
39
+ const specificExamples: Record<string, readonly CommandExample[]> = {
40
+ "records query": buildCommandExamples([
41
+ {
42
+ description: "Query records with filters, sort and pagination",
43
+ command: 'zinkee --json records query contacts --select id,name --where "status eq active" --sort createdAt:desc --limit 20 --offset 0',
44
+ },
45
+ ]),
46
+ "records create": buildCommandExamples([
47
+ {
48
+ description: "Create a record with simple and structured values",
49
+ command: `zinkee --json records create contacts --set name=Ana --set-json meta='{"tier":"gold"}'`,
50
+ },
51
+ ]),
52
+ "files download": buildCommandExamples([
53
+ {
54
+ description: "Download a file to disk in JSON mode",
55
+ command: `zinkee --json files download ${SAMPLE_UUID} --output ./download.bin`,
56
+ },
57
+ ]),
58
+ "navigation resources move": buildCommandExamples([
59
+ {
60
+ description: "Move a resource into a navigation folder by exact folder name",
61
+ command: `zinkee --json navigation resources move ${SAMPLE_UUID} --type schema --to Finance`,
62
+ },
63
+ ]),
64
+ "teamspace resources publish": buildCommandExamples([
65
+ {
66
+ description: "Publish a resource into a teamspace folder",
67
+ command: `zinkee --json teamspace resources publish ${SAMPLE_UUID} --to "Customer Success"`,
68
+ },
69
+ ]),
70
+ "displays widgets create": buildCommandExamples([
71
+ {
72
+ description: "Create a chart widget through the generic --type path",
73
+ command: 'zinkee --json displays widgets create display-1 --type chart --name "Revenue" --chart-type bar',
74
+ },
75
+ ]),
76
+ "automations create": buildCommandExamples([
77
+ {
78
+ description:
79
+ "Create a scheduled automation with a six-field cron expression. This is the safest first example because it does not depend on workspace schemas.",
80
+ command: 'zinkee --json automations create --name "Nightly invoice sync" --trigger-type scheduled --cron "0 15 10 * * 1"',
81
+ raw: `{
82
+ "name": "Nightly invoice sync",
83
+ "trigger": {
84
+ "type": "scheduled",
85
+ "config": {
86
+ "cronExpression": "0 15 10 * * 1"
87
+ }
88
+ }
89
+ }`,
90
+ },
91
+ {
92
+ description:
93
+ "Create a record-created automation using a real schema slug or UUID. Replace 'leads' with a schema from 'zinkee --json schemas list'.",
94
+ command: 'zinkee --json automations create --name "Lead welcome" --trigger-type record_created --trigger-schema leads',
95
+ raw: `{
96
+ "name": "Lead welcome",
97
+ "trigger": {
98
+ "type": "record_created",
99
+ "config": {
100
+ "schemaId": "leads"
101
+ }
102
+ }
103
+ }`,
104
+ },
105
+ {
106
+ description:
107
+ "Create a record-changed automation using real schema and field slugs or UUIDs. Replace 'orders' and 'status' with values from your workspace.",
108
+ command: 'zinkee --json automations create --name "Order status sync" --trigger-type record_changed --trigger-schema orders --trigger-field status',
109
+ raw: `{
110
+ "name": "Order status sync",
111
+ "trigger": {
112
+ "type": "record_changed",
113
+ "config": {
114
+ "schemaId": "orders",
115
+ "fieldIds": ["status"]
116
+ }
117
+ }
118
+ }`,
119
+ },
120
+ ]),
121
+ "automations update": buildCommandExamples([
122
+ {
123
+ description: "Patch automation metadata using the backend set/unset contract",
124
+ command:
125
+ 'zinkee --json automations update automation-1 --raw \'{"set":{"description":"Updated by CLI docs"},"unset":["folderId"]}\'',
126
+ raw: `{
127
+ "set": {
128
+ "description": "Updated by CLI docs"
129
+ },
130
+ "unset": ["folderId"]
131
+ }`,
132
+ },
133
+ ]),
134
+ "automations trigger set": buildCommandExamples([
135
+ {
136
+ description: "Replace the trigger with a scheduled trigger",
137
+ command: 'zinkee --json automations trigger set automation-1 --trigger-type scheduled --cron "0 0 9 * * *"',
138
+ raw: `{
139
+ "type": "scheduled",
140
+ "config": {
141
+ "cronExpression": "0 0 9 * * *"
142
+ }
143
+ }`,
144
+ },
145
+ {
146
+ description: "Replace the trigger with a record-changed trigger and a literal condition",
147
+ command:
148
+ 'zinkee --json automations trigger set automation-1 --trigger-type record_changed --trigger-schema orders --trigger-field status --condition status:eq:approved',
149
+ raw: `{
150
+ "type": "record_changed",
151
+ "config": {
152
+ "schemaId": "orders",
153
+ "fieldIds": ["status"],
154
+ "conditions": [
155
+ {
156
+ "field": "status",
157
+ "comparator": "eq",
158
+ "values": [
159
+ {
160
+ "rawValue": "approved",
161
+ "source": null
162
+ }
163
+ ]
164
+ }
165
+ ]
166
+ }
167
+ }`,
168
+ },
169
+ {
170
+ description: "Replace the trigger with a webhook trigger using an existing webhook endpoint id",
171
+ command:
172
+ 'zinkee --json automations trigger set automation-1 --trigger-type webhook --webhook-endpoint-id 550e8400-e29b-41d4-a716-446655440001',
173
+ raw: `{
174
+ "type": "webhook",
175
+ "config": {
176
+ "webhookEndpointId": "550e8400-e29b-41d4-a716-446655440001"
177
+ }
178
+ }`,
179
+ },
180
+ ]),
181
+ "automations actions add": buildCommandExamples([
182
+ {
183
+ description: "Add a create-record action with literal and trigger-based field mappings",
184
+ command:
185
+ 'zinkee --json automations actions add automation-1 --type create_record --name "Create follow-up" --target-schema followups --map status=pending --map-json owner=\'{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}\'',
186
+ raw: `{
187
+ "type": "create_record",
188
+ "name": "Create follow-up",
189
+ "config": {
190
+ "targetSchemaId": "followups",
191
+ "fieldMapping": {
192
+ "status": {
193
+ "rawValue": "pending",
194
+ "source": null
195
+ },
196
+ "owner": {
197
+ "rawValue": null,
198
+ "source": {
199
+ "type": "TRIGGER",
200
+ "id": "payload_value"
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }`,
206
+ },
207
+ {
208
+ description: "Add a search-records action with a trigger-based condition",
209
+ command:
210
+ 'zinkee --json automations actions add automation-1 --type search_records --name "Find duplicates" --target-schema followups --condition-json \'{"field":"email","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}]}\'',
211
+ raw: `{
212
+ "type": "search_records",
213
+ "name": "Find duplicates",
214
+ "config": {
215
+ "targetSchemaId": "followups",
216
+ "conditions": [
217
+ {
218
+ "field": "email",
219
+ "comparator": "eq",
220
+ "values": [
221
+ {
222
+ "rawValue": null,
223
+ "source": {
224
+ "type": "TRIGGER",
225
+ "id": "payload_value"
226
+ }
227
+ }
228
+ ]
229
+ }
230
+ ]
231
+ }
232
+ }`,
233
+ },
234
+ {
235
+ description: "Add an HTTP request action through the generic --type path",
236
+ command:
237
+ 'zinkee --json automations actions add automation-1 --type http_request --name Notify --method POST --url https://example.com/hook --content-type application/json',
238
+ raw: `{
239
+ "type": "http_request",
240
+ "name": "Notify",
241
+ "config": {
242
+ "method": "POST",
243
+ "url": "https://example.com/hook",
244
+ "contentType": "application/json"
245
+ }
246
+ }`,
247
+ },
248
+ {
249
+ description:
250
+ "Add an execute-plugin action with structured plugin arguments. Replace 'clone-budget' with an active id from 'zinkee --json automations plugins list'.",
251
+ command:
252
+ 'zinkee --json automations actions add automation-1 --type execute_plugin --name "Run plugin" --plugin clone-budget --arg mode=safe --arg-json payload=\'{"dryRun":true}\'',
253
+ raw: `{
254
+ "type": "execute_plugin",
255
+ "name": "Run plugin",
256
+ "config": {
257
+ "pluginId": "clone-budget",
258
+ "pluginArgs": {
259
+ "mode": "safe",
260
+ "payload": {
261
+ "dryRun": true
262
+ }
263
+ }
264
+ }
265
+ }`,
266
+ },
267
+ ]),
268
+ "automations actions update": buildCommandExamples([
269
+ {
270
+ description: "Update a record action using the trigger record without sending conditions",
271
+ command:
272
+ 'zinkee --json automations actions update automation-1 action-1 --type update_record --name "Update trigger record" --target-schema followups --map status=ready --use-trigger-record',
273
+ raw: `{
274
+ "type": "update_record",
275
+ "name": "Update trigger record",
276
+ "config": {
277
+ "targetSchemaId": "followups",
278
+ "fieldMapping": {
279
+ "status": {
280
+ "rawValue": "ready",
281
+ "source": null
282
+ }
283
+ },
284
+ "useTriggerRecord": true
285
+ }
286
+ }`,
287
+ },
288
+ ]),
289
+ "automations flow set": buildCommandExamples([
290
+ {
291
+ description: "Set flow entry actions and transitions",
292
+ command: 'zinkee --json automations flow set automation-1 --entry action-1 --transition action-1:action-2',
293
+ raw: `{
294
+ "entryActionIds": ["action-1"],
295
+ "transitions": [
296
+ {
297
+ "fromActionId": "action-1",
298
+ "toActionId": "action-2"
299
+ }
300
+ ]
301
+ }`,
302
+ },
303
+ ]),
304
+ "automations webhook set": buildCommandExamples([
305
+ {
306
+ description:
307
+ "Configure webhook ingestion paths and field mappings for a webhook automation. This config is separate from the webhook trigger itself.",
308
+ command:
309
+ 'zinkee --json automations webhook set automation-1 --active --idempotency-key-path "$.id" --event-type-path "$.type" --allowed-event-type frontend.demo.created --field-mapping \'{"variable":"external_id","jsonPath":"$.data.id"}\'',
310
+ raw: `{
311
+ "active": true,
312
+ "idempotencyKeyJsonPath": "$.id",
313
+ "eventTypeJsonPath": "$.type",
314
+ "allowedEventTypes": ["frontend.demo.created"],
315
+ "fieldMappings": [
316
+ {
317
+ "variable": "external_id",
318
+ "jsonPath": "$.data.id"
319
+ }
320
+ ]
321
+ }`,
322
+ },
323
+ ]),
324
+ "automations connections create": buildCommandExamples([
325
+ {
326
+ description: "Create a stored workspace connection for HTTP request actions",
327
+ command:
328
+ 'zinkee --json automations connections create --name "ERP API Key" --type api_key --scope workspace --config \'{"headerName":"Authorization"}\' --secret apiKey=super-secret --active',
329
+ raw: `{
330
+ "name": "ERP API Key",
331
+ "type": "api_key",
332
+ "scope": "workspace",
333
+ "config": {
334
+ "headerName": "Authorization"
335
+ },
336
+ "secret": {
337
+ "apiKey": "super-secret"
338
+ },
339
+ "active": true
340
+ }`,
341
+ },
342
+ ]),
343
+ "automations connections update": buildCommandExamples([
344
+ {
345
+ description: "Patch an existing stored connection",
346
+ command:
347
+ 'zinkee --json automations connections update connection-1 --raw \'{"set":{"name":"ERP API Key Updated","config":{"headerName":"Authorization"}}}\'',
348
+ raw: `{
349
+ "set": {
350
+ "name": "ERP API Key Updated",
351
+ "config": {
352
+ "headerName": "Authorization"
353
+ }
354
+ }
355
+ }`,
356
+ },
357
+ ]),
358
+ };
359
+
360
+ function fallbackExamples(commandName: string): readonly CommandExample[] {
361
+ switch (commandName) {
362
+ case "profiles list":
363
+ return [{ description: "List configured profiles", command: "zinkee profiles list" }];
364
+ case "profiles get":
365
+ return [{ description: "Show a profile", command: "zinkee profiles get prod" }];
366
+ case "profiles add":
367
+ return [{ description: "Add a profile", command: 'zinkee profiles add local --api-key token --base-url http://localhost:8088' }];
368
+ case "config validate":
369
+ return [{ description: "Validate config", command: "zinkee --json config validate" }];
370
+ case "schemas list":
371
+ return [{ description: "List schemas", command: "zinkee --json schemas list" }];
372
+ case "schemas create":
373
+ return [{ description: "Create a schema", command: 'zinkee --json schemas create --name Contacts --slug contacts --auditable --commentable' }];
374
+ case "comments create":
375
+ return [{ description: "Create a text comment", command: `zinkee --json comments create contacts ${SAMPLE_UUID} --text "Hello world"` }];
376
+ case "navigation folders create":
377
+ return [{ description: "Create a navigation folder", command: 'zinkee --json navigation folders create --name Finance --order 10' }];
378
+ case "teamspace folders list":
379
+ return [{ description: "List teamspace folders with resources", command: "zinkee --json teamspace folders list --include-resources" }];
380
+ case "displays create":
381
+ return [
382
+ { description: "Create a display", command: 'zinkee --json displays create --name Pipeline --template widget_row_tabs' },
383
+ {
384
+ description: "Create a freeform display with responsive layout data",
385
+ command: 'zinkee --json displays create --name "Control Proyecto Freeform" --template freeform --freeform-layouts \'{"lg":[{"i":"widget-1","x":0,"y":0,"w":6,"h":5}]}\'',
386
+ },
387
+ ];
388
+ case "automations list":
389
+ return [{ description: "List automations", command: "zinkee --json automations list" }];
390
+ default:
391
+ return [];
392
+ }
393
+ }
394
+
395
+ function buildSchemaFieldCreateExamples(type?: string): readonly CommandExample[] {
396
+ const definitions = getSchemaFieldTypeDefinitions();
397
+ const selectedDefinition = resolveSchemaFieldType(type);
398
+ const filteredDefinitions = selectedDefinition
399
+ ? definitions.filter((definition) => definition.key === selectedDefinition.key)
400
+ : definitions;
401
+
402
+ return buildCommandExamples(
403
+ filteredDefinitions.flatMap((definition) => {
404
+ if (definition.key !== "number") {
405
+ return [{
406
+ description: `Create a ${definition.key} field`,
407
+ command: `zinkee --json schemas fields create contacts --raw '${JSON.stringify(definition.examplePayload)}'`,
408
+ }];
409
+ }
410
+
411
+ return [
412
+ {
413
+ description: "Create a plain number field",
414
+ command:
415
+ 'zinkee --json schemas fields create contacts --raw \'{"type":"number","slug":"amount","label":"Amount"}\'',
416
+ },
417
+ {
418
+ description: "Create a number field formatted like Moneda",
419
+ command: `zinkee --json schemas fields create contacts --raw '${JSON.stringify(definition.examplePayload)}'`,
420
+ },
421
+ ];
422
+ }),
423
+ );
424
+ }
425
+
426
+ export function getCommandExamples(
427
+ commandName: string,
428
+ filters: { type?: string } = {},
429
+ ): readonly CommandExample[] {
430
+ if (commandName === "schemas fields create") {
431
+ return buildSchemaFieldCreateExamples(filters.type);
432
+ }
433
+
434
+ return specificExamples[commandName] ?? fallbackExamples(commandName);
435
+ }
436
+
437
+ interface ExampleAutomationContext {
438
+ automationsApi: AutomationsApi;
439
+ schemasApi: SchemasApi;
440
+ }
441
+
442
+ interface SchemaFieldReference {
443
+ schemaId: string;
444
+ schemaRef: string;
445
+ fieldId: string;
446
+ fieldRef: string;
447
+ }
448
+
449
+ interface AutomationActionReference {
450
+ automationId: string;
451
+ actionId: string;
452
+ }
453
+
454
+ function selector(value: string): NormalizedUuidOrSlug {
455
+ return {
456
+ kind: "uuid",
457
+ value,
458
+ };
459
+ }
460
+
461
+ async function createExampleAutomationContext(
462
+ options: ExampleContextOptions = {},
463
+ ): Promise<ExampleAutomationContext | null> {
464
+ if (options.enabled !== true) {
465
+ return null;
466
+ }
467
+
468
+ try {
469
+ const config = readConfig(options.configPath ?? defaultConfigPath);
470
+ const runtime = resolveRuntimeContext(config, options.overrides);
471
+
472
+ if (!runtime.baseUrl) {
473
+ return null;
474
+ }
475
+
476
+ const client = new ApiClient({
477
+ baseUrl: runtime.baseUrl,
478
+ apiKey: runtime.apiKey,
479
+ });
480
+
481
+ return {
482
+ automationsApi: new AutomationsApi(client),
483
+ schemasApi: new SchemasApi(client),
484
+ };
485
+ } catch {
486
+ return null;
487
+ }
488
+ }
489
+
490
+ function referenceValue(value: { slug?: string; id?: string }): string | undefined {
491
+ return value.slug ?? value.id;
492
+ }
493
+
494
+ function pickExampleField(fields: readonly ApiSchemaField[]): ApiSchemaField | undefined {
495
+ return fields.find((field) => !field.required || field.slug || field.id) ?? fields[0];
496
+ }
497
+
498
+ async function findSchemaFieldReference(
499
+ context: ExampleAutomationContext,
500
+ ): Promise<SchemaFieldReference | null> {
501
+ const schemas = await context.schemasApi.listSchemas();
502
+
503
+ for (const schema of schemas) {
504
+ if (typeof schema.id !== "string") {
505
+ continue;
506
+ }
507
+
508
+ const fullSchema = await context.schemasApi.getSchema(selector(schema.id));
509
+ const fields = Array.isArray(fullSchema.fields) ? fullSchema.fields : [];
510
+ const field = pickExampleField(fields);
511
+ const schemaRef = referenceValue(schema);
512
+ const fieldRef = field ? referenceValue(field) : undefined;
513
+ const fieldId = typeof field?.id === "string" ? field.id : undefined;
514
+
515
+ if (!schemaRef || !fieldRef || !fieldId) {
516
+ continue;
517
+ }
518
+
519
+ return {
520
+ schemaId: schema.id,
521
+ schemaRef,
522
+ fieldId,
523
+ fieldRef,
524
+ };
525
+ }
526
+
527
+ return null;
528
+ }
529
+
530
+ async function findAutomationId(context: ExampleAutomationContext): Promise<string | null> {
531
+ const automations = await context.automationsApi.listAutomations();
532
+ const first = automations.find((automation) => typeof automation.id === "string");
533
+ return typeof first?.id === "string" ? first.id : null;
534
+ }
535
+
536
+ async function findAutomationWithAction(
537
+ context: ExampleAutomationContext,
538
+ ): Promise<AutomationActionReference | null> {
539
+ const automations = await context.automationsApi.listAutomations();
540
+
541
+ for (const automation of automations) {
542
+ if (typeof automation.id !== "string") {
543
+ continue;
544
+ }
545
+
546
+ const actions = await context.automationsApi.listActions(selector(automation.id));
547
+ const firstAction = actions.find((action) => typeof action.id === "string");
548
+
549
+ if (typeof firstAction?.id === "string") {
550
+ return {
551
+ automationId: automation.id,
552
+ actionId: firstAction.id,
553
+ };
554
+ }
555
+ }
556
+
557
+ return null;
558
+ }
559
+
560
+ async function buildDynamicAutomationExamples(
561
+ commandName: string,
562
+ options: ExampleContextOptions = {},
563
+ ): Promise<readonly CommandExample[]> {
564
+ const context = await createExampleAutomationContext(options);
565
+
566
+ if (!context) {
567
+ return [];
568
+ }
569
+
570
+ const schemaField = await findSchemaFieldReference(context);
571
+ const automationId = await findAutomationId(context);
572
+ const automationWithAction = await findAutomationWithAction(context);
573
+ const plugins = await context.automationsApi.listPlugins();
574
+ const pluginId = typeof plugins[0]?.id === "string" ? plugins[0].id : undefined;
575
+ const connections = await context.automationsApi.listConnections();
576
+ const connectionId = typeof connections[0]?.id === "string" ? connections[0].id : undefined;
577
+
578
+ switch (commandName) {
579
+ case "automations create":
580
+ if (!schemaField) {
581
+ return [];
582
+ }
583
+ return buildCommandExamples([
584
+ {
585
+ description:
586
+ "Create a scheduled automation with a six-field cron expression. This is the safest first example because it does not depend on workspace schemas.",
587
+ command:
588
+ 'zinkee --json automations create --name "Nightly invoice sync" --trigger-type scheduled --cron "0 15 10 * * 1"',
589
+ raw: `{
590
+ "name": "Nightly invoice sync",
591
+ "trigger": {
592
+ "type": "scheduled",
593
+ "config": {
594
+ "cronExpression": "0 15 10 * * 1"
595
+ }
596
+ }
597
+ }`,
598
+ },
599
+ {
600
+ description:
601
+ `Create a record-created automation using a real schema reference from this workspace (${schemaField.schemaRef}).`,
602
+ command: `zinkee --json automations create --name "Record created demo" --trigger-type record_created --trigger-schema ${schemaField.schemaRef}`,
603
+ raw: `{
604
+ "name": "Record created demo",
605
+ "trigger": {
606
+ "type": "record_created",
607
+ "config": {
608
+ "schemaId": "${schemaField.schemaRef}"
609
+ }
610
+ }
611
+ }`,
612
+ },
613
+ {
614
+ description:
615
+ `Create a record-changed automation using a real schema and field reference from this workspace (${schemaField.schemaRef} / ${schemaField.fieldRef}).`,
616
+ command: `zinkee --json automations create --name "Record changed demo" --trigger-type record_changed --trigger-schema ${schemaField.schemaRef} --trigger-field ${schemaField.fieldRef}`,
617
+ raw: `{
618
+ "name": "Record changed demo",
619
+ "trigger": {
620
+ "type": "record_changed",
621
+ "config": {
622
+ "schemaId": "${schemaField.schemaRef}",
623
+ "fieldIds": ["${schemaField.fieldRef}"]
624
+ }
625
+ }
626
+ }`,
627
+ },
628
+ ]);
629
+ case "automations trigger set":
630
+ if (!automationId || !schemaField) {
631
+ return [];
632
+ }
633
+ return buildCommandExamples([
634
+ {
635
+ description: "Replace the trigger with a scheduled trigger on a real automation from this workspace.",
636
+ command: `zinkee --json automations trigger set ${automationId} --trigger-type scheduled --cron "0 0 9 * * *"`,
637
+ raw: `{
638
+ "type": "scheduled",
639
+ "config": {
640
+ "cronExpression": "0 0 9 * * *"
641
+ }
642
+ }`,
643
+ },
644
+ {
645
+ description:
646
+ `Replace the trigger with a record-changed trigger using a real schema and field (${schemaField.schemaRef} / ${schemaField.fieldRef}).`,
647
+ command: `zinkee --json automations trigger set ${automationId} --trigger-type record_changed --trigger-schema ${schemaField.schemaRef} --trigger-field ${schemaField.fieldRef} --condition ${schemaField.fieldRef}:eq:approved`,
648
+ raw: `{
649
+ "type": "record_changed",
650
+ "config": {
651
+ "schemaId": "${schemaField.schemaRef}",
652
+ "fieldIds": ["${schemaField.fieldRef}"],
653
+ "conditions": [
654
+ {
655
+ "field": "${schemaField.fieldRef}",
656
+ "comparator": "eq",
657
+ "values": [
658
+ {
659
+ "rawValue": "approved",
660
+ "source": null
661
+ }
662
+ ]
663
+ }
664
+ ]
665
+ }
666
+ }`,
667
+ },
668
+ ]);
669
+ case "automations actions add":
670
+ if (!automationId || !schemaField) {
671
+ return [];
672
+ }
673
+ return buildCommandExamples([
674
+ {
675
+ description: "Add a create-record action with literal and trigger-based mappings from this workspace.",
676
+ command: `zinkee --json automations actions add ${automationId} --type create_record --name "Create follow-up" --target-schema ${schemaField.schemaRef} --map ${schemaField.fieldRef}=pending --map-json owner='{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}'`,
677
+ raw: `{
678
+ "type": "create_record",
679
+ "name": "Create follow-up",
680
+ "config": {
681
+ "targetSchemaId": "${schemaField.schemaRef}",
682
+ "fieldMapping": {
683
+ "${schemaField.fieldRef}": {
684
+ "rawValue": "pending",
685
+ "source": null
686
+ },
687
+ "owner": {
688
+ "rawValue": null,
689
+ "source": {
690
+ "type": "TRIGGER",
691
+ "id": "payload_value"
692
+ }
693
+ }
694
+ }
695
+ }
696
+ }`,
697
+ },
698
+ {
699
+ description: "Add a search-records action with a trigger-based condition from this workspace.",
700
+ command: `zinkee --json automations actions add ${automationId} --type search_records --name "Find duplicates" --target-schema ${schemaField.schemaRef} --condition-json '{"field":"${schemaField.fieldRef}","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}]}'`,
701
+ raw: `{
702
+ "type": "search_records",
703
+ "name": "Find duplicates",
704
+ "config": {
705
+ "targetSchemaId": "${schemaField.schemaRef}",
706
+ "conditions": [
707
+ {
708
+ "field": "${schemaField.fieldRef}",
709
+ "comparator": "eq",
710
+ "values": [
711
+ {
712
+ "rawValue": null,
713
+ "source": {
714
+ "type": "TRIGGER",
715
+ "id": "payload_value"
716
+ }
717
+ }
718
+ ]
719
+ }
720
+ ]
721
+ }
722
+ }`,
723
+ },
724
+ {
725
+ description: "Add an HTTP request action through the generic --type path.",
726
+ command: `zinkee --json automations actions add ${automationId} --type http_request --name Notify --method POST --url https://example.com/hook --content-type application/json`,
727
+ raw: `{
728
+ "type": "http_request",
729
+ "name": "Notify",
730
+ "config": {
731
+ "method": "POST",
732
+ "url": "https://example.com/hook",
733
+ "contentType": "application/json"
734
+ }
735
+ }`,
736
+ },
737
+ ...(pluginId
738
+ ? buildCommandExamples([
739
+ {
740
+ description:
741
+ `Add an execute-plugin action using an active plugin from this workspace (${pluginId}).`,
742
+ command: `zinkee --json automations actions add ${automationId} --type execute_plugin --name "Run plugin" --plugin ${pluginId} --arg mode=safe --arg-json payload='{"dryRun":true}'`,
743
+ raw: `{
744
+ "type": "execute_plugin",
745
+ "name": "Run plugin",
746
+ "config": {
747
+ "pluginId": "${pluginId}",
748
+ "pluginArgs": {
749
+ "mode": "safe",
750
+ "payload": {
751
+ "dryRun": true
752
+ }
753
+ }
754
+ }
755
+ }`,
756
+ },
757
+ ])
758
+ : []),
759
+ ]);
760
+ case "automations actions update":
761
+ if (!automationWithAction || !schemaField) {
762
+ return [];
763
+ }
764
+ return buildCommandExamples([
765
+ {
766
+ description: "Update a record action on a real automation/action pair using the trigger record.",
767
+ command: `zinkee --json automations actions update ${automationWithAction.automationId} ${automationWithAction.actionId} --type update_record --name "Update trigger record" --target-schema ${schemaField.schemaRef} --map ${schemaField.fieldRef}=ready --use-trigger-record`,
768
+ raw: `{
769
+ "type": "update_record",
770
+ "name": "Update trigger record",
771
+ "config": {
772
+ "targetSchemaId": "${schemaField.schemaRef}",
773
+ "fieldMapping": {
774
+ "${schemaField.fieldRef}": {
775
+ "rawValue": "ready",
776
+ "source": null
777
+ }
778
+ },
779
+ "useTriggerRecord": true
780
+ }
781
+ }`,
782
+ },
783
+ ]);
784
+ case "automations flow set":
785
+ if (!automationWithAction) {
786
+ return [];
787
+ }
788
+ return buildCommandExamples([
789
+ {
790
+ description: "Set flow entry actions using a real automation/action pair from this workspace.",
791
+ command: `zinkee --json automations flow set ${automationWithAction.automationId} --entry ${automationWithAction.actionId}`,
792
+ raw: `{
793
+ "entryActionIds": ["${automationWithAction.actionId}"],
794
+ "transitions": []
795
+ }`,
796
+ },
797
+ ]);
798
+ case "automations connections create":
799
+ return buildCommandExamples([
800
+ {
801
+ description: "Create a stored workspace connection for HTTP request actions.",
802
+ command:
803
+ 'zinkee --json automations connections create --name "ERP API Key" --type api_key --scope workspace --config \'{"headerName":"Authorization"}\' --secret apiKey=super-secret --active',
804
+ raw: `{
805
+ "name": "ERP API Key",
806
+ "type": "api_key",
807
+ "scope": "workspace",
808
+ "config": {
809
+ "headerName": "Authorization"
810
+ },
811
+ "secret": {
812
+ "apiKey": "super-secret"
813
+ },
814
+ "active": true
815
+ }`,
816
+ },
817
+ ]);
818
+ case "automations connections update":
819
+ if (!connectionId) {
820
+ return [];
821
+ }
822
+ return buildCommandExamples([
823
+ {
824
+ description: "Patch an existing stored connection from this workspace.",
825
+ command: `zinkee --json automations connections update ${connectionId} --raw '{"set":{"name":"ERP API Key Updated","config":{"headerName":"Authorization"}}}'`,
826
+ raw: `{
827
+ "set": {
828
+ "name": "ERP API Key Updated",
829
+ "config": {
830
+ "headerName": "Authorization"
831
+ }
832
+ }
833
+ }`,
834
+ },
835
+ ]);
836
+ default:
837
+ return [];
838
+ }
839
+ }
840
+
841
+ export async function getResolvedCommandExamples(
842
+ commandName: string,
843
+ filters: { type?: string } = {},
844
+ options: ExampleContextOptions = {},
845
+ ): Promise<readonly CommandExample[]> {
846
+ const dynamicExamples = commandName.startsWith("automations")
847
+ ? await buildDynamicAutomationExamples(commandName, options)
848
+ : [];
849
+
850
+ if (dynamicExamples.length > 0) {
851
+ return dynamicExamples;
852
+ }
853
+
854
+ return getCommandExamples(commandName, filters);
855
+ }