zinkee 0.1.36 → 0.1.37

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/index.js CHANGED
@@ -275,6 +275,41 @@ var commandRegistry = {
275
275
  supportsRaw: false,
276
276
  output: writeOutput
277
277
  },
278
+ "displays folders list": {
279
+ command: "displays folders list",
280
+ accessLevel: "read",
281
+ supportsExample: true,
282
+ supportsRaw: false,
283
+ output: readOutput
284
+ },
285
+ "displays folders create": {
286
+ command: "displays folders create",
287
+ accessLevel: "write",
288
+ supportsExample: true,
289
+ supportsRaw: false,
290
+ output: writeOutput
291
+ },
292
+ "displays folders update": {
293
+ command: "displays folders update",
294
+ accessLevel: "write",
295
+ supportsExample: true,
296
+ supportsRaw: false,
297
+ output: writeOutput
298
+ },
299
+ "displays folders delete": {
300
+ command: "displays folders delete",
301
+ accessLevel: "destructive",
302
+ supportsExample: true,
303
+ supportsRaw: false,
304
+ output: writeOutput
305
+ },
306
+ "displays folders move": {
307
+ command: "displays folders move",
308
+ accessLevel: "write",
309
+ supportsExample: true,
310
+ supportsRaw: false,
311
+ output: writeOutput
312
+ },
278
313
  "displays layout get": {
279
314
  command: "displays layout get",
280
315
  accessLevel: "read",
@@ -436,6 +471,62 @@ var commandRegistry = {
436
471
  supportsRaw: false,
437
472
  output: writeOutput
438
473
  },
474
+ "document-templates list": {
475
+ command: "document-templates list",
476
+ accessLevel: "read",
477
+ supportsExample: true,
478
+ supportsRaw: false,
479
+ output: readOutput
480
+ },
481
+ "document-templates get": {
482
+ command: "document-templates get",
483
+ accessLevel: "read",
484
+ supportsExample: true,
485
+ supportsRaw: false,
486
+ output: readOutput
487
+ },
488
+ "document-templates create": {
489
+ command: "document-templates create",
490
+ accessLevel: "write",
491
+ supportsExample: true,
492
+ supportsRaw: true,
493
+ output: writeOutput
494
+ },
495
+ "document-templates update": {
496
+ command: "document-templates update",
497
+ accessLevel: "write",
498
+ supportsExample: true,
499
+ supportsRaw: false,
500
+ output: writeOutput
501
+ },
502
+ "document-templates delete": {
503
+ command: "document-templates delete",
504
+ accessLevel: "destructive",
505
+ supportsExample: true,
506
+ supportsRaw: false,
507
+ output: writeOutput
508
+ },
509
+ "document-templates assets add": {
510
+ command: "document-templates assets add",
511
+ accessLevel: "write",
512
+ supportsExample: true,
513
+ supportsRaw: false,
514
+ output: writeOutput
515
+ },
516
+ "document-templates assets delete": {
517
+ command: "document-templates assets delete",
518
+ accessLevel: "destructive",
519
+ supportsExample: true,
520
+ supportsRaw: false,
521
+ output: writeOutput
522
+ },
523
+ "document-templates assets download": {
524
+ command: "document-templates assets download",
525
+ accessLevel: "read",
526
+ supportsExample: true,
527
+ supportsRaw: false,
528
+ output: "binary"
529
+ },
439
530
  "files upload": {
440
531
  command: "files upload",
441
532
  accessLevel: "write",
@@ -576,6 +667,13 @@ var commandRegistry = {
576
667
  supportsRaw: false,
577
668
  output: writeOutput
578
669
  },
670
+ "schemas set-unique-fields": {
671
+ command: "schemas set-unique-fields",
672
+ accessLevel: "write",
673
+ supportsExample: true,
674
+ supportsRaw: false,
675
+ output: writeOutput
676
+ },
579
677
  "schemas delete": {
580
678
  command: "schemas delete",
581
679
  accessLevel: "destructive",
@@ -2311,6 +2409,7 @@ var specificExamples = {
2311
2409
  },
2312
2410
  "auditable": true,
2313
2411
  "commentable": true,
2412
+ "highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
2314
2413
  "fields": [
2315
2414
  {
2316
2415
  "id": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
@@ -2325,7 +2424,8 @@ var specificExamples = {
2325
2424
  "conditionalFormatting": []
2326
2425
  }
2327
2426
  }
2328
- ]
2427
+ ],
2428
+ "uniqueFields": []
2329
2429
  }`
2330
2430
  },
2331
2431
  {
@@ -2341,6 +2441,86 @@ var specificExamples = {
2341
2441
  command: "zinkee --json schemas update contacts --auditable true",
2342
2442
  raw: `{
2343
2443
  "auditable": true
2444
+ }`
2445
+ },
2446
+ {
2447
+ description: "Set the schema's highlighted (main) field \u2014 the field whose value names each record in the UI (activity-panel title and detail-view record list).",
2448
+ command: "zinkee --json schemas update contacts --highlighted-field contact-name",
2449
+ raw: `{
2450
+ "highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
2451
+ }`
2452
+ }
2453
+ ]),
2454
+ "schemas set-unique-fields": buildCommandExamples([
2455
+ {
2456
+ description: "Set a composite unique constraint: the listed fields must be unique together across records.",
2457
+ command: "zinkee --json schemas set-unique-fields contacts --field contact-name --field notes",
2458
+ raw: `{
2459
+ "fieldIds": [
2460
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34",
2461
+ "1d2e3f4a-5b6c-7d8e-9f01-23456789abcd"
2462
+ ]
2463
+ }`,
2464
+ response: `{
2465
+ "id": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
2466
+ "name": "Contacts",
2467
+ "slug": "contacts",
2468
+ "folder": {
2469
+ "id": "7f4e7c25-d8b2-4c1a-9f3e-6a7b5c8d9e1f",
2470
+ "name": "Customers",
2471
+ "isRoot": false
2472
+ },
2473
+ "auditable": true,
2474
+ "commentable": true,
2475
+ "highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
2476
+ "fields": [
2477
+ {
2478
+ "id": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
2479
+ "type": "TextField",
2480
+ "slug": "contact-name",
2481
+ "label": "Customer Name",
2482
+ "required": true,
2483
+ "system": false,
2484
+ "config": {
2485
+ "textType": "NORMAL",
2486
+ "defaultValue": "Unknown",
2487
+ "conditionalFormatting": []
2488
+ }
2489
+ },
2490
+ {
2491
+ "id": "1d2e3f4a-5b6c-7d8e-9f01-23456789abcd",
2492
+ "type": "TextField",
2493
+ "slug": "notes",
2494
+ "label": "Notes",
2495
+ "required": false,
2496
+ "system": false,
2497
+ "config": {
2498
+ "textType": "LONG",
2499
+ "defaultValue": null,
2500
+ "conditionalFormatting": []
2501
+ }
2502
+ }
2503
+ ],
2504
+ "uniqueFields": [
2505
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34",
2506
+ "1d2e3f4a-5b6c-7d8e-9f01-23456789abcd"
2507
+ ]
2508
+ }`
2509
+ },
2510
+ {
2511
+ description: "Set a single-field unique constraint.",
2512
+ command: "zinkee --json schemas set-unique-fields contacts --field contact-name",
2513
+ raw: `{
2514
+ "fieldIds": [
2515
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34"
2516
+ ]
2517
+ }`
2518
+ },
2519
+ {
2520
+ description: "Clear the unique constraint (no field must be unique anymore).",
2521
+ command: "zinkee --json schemas set-unique-fields contacts --clear",
2522
+ raw: `{
2523
+ "fieldIds": []
2344
2524
  }`
2345
2525
  }
2346
2526
  ]),
@@ -2570,7 +2750,7 @@ var specificExamples = {
2570
2750
  },
2571
2751
  {
2572
2752
  description: "Filter by status with a free-text search term.",
2573
- command: `zinkee --json records query contacts --select id,contact-name,status --where "status in OPEN" --limit 25 --raw '{"search":{"term":"garcia"}}'`,
2753
+ command: `zinkee --json records query contacts --select id,contact-name,status --where "status in OPEN" --search garcia --limit 25`,
2574
2754
  raw: `{
2575
2755
  "select": ["id", "contact-name", "status"],
2576
2756
  "where": [
@@ -2652,35 +2832,78 @@ var specificExamples = {
2652
2832
  ]),
2653
2833
  "records create": buildCommandExamples([
2654
2834
  {
2655
- description: "Minimal create using just two plain string fields.",
2835
+ description: "Create a single record with --set/--set-json (the single-record flags build one record, sent as a one-element 'records' array).",
2656
2836
  command: `zinkee --json records create contacts --set contact-name="Ada Lovelace" --set contact-email=ada@example.com`,
2657
2837
  raw: `{
2658
- "values": {
2659
- "contact-name": "Ada Lovelace",
2660
- "contact-email": "ada@example.com"
2661
- }
2838
+ "records": [
2839
+ {
2840
+ "contact-name": "Ada Lovelace",
2841
+ "contact-email": "ada@example.com"
2842
+ }
2843
+ ]
2662
2844
  }`,
2663
2845
  response: `{
2664
- "recordId": "550e8400-e29b-41d4-a716-446655440000",
2665
- "values": {
2666
- "contact-name": "Ada Lovelace",
2667
- "contact-email": "ada@example.com"
2668
- }
2846
+ "created": [
2847
+ "550e8400-e29b-41d4-a716-446655440000"
2848
+ ],
2849
+ "errors": []
2669
2850
  }`
2670
2851
  },
2671
2852
  {
2672
- description: "Create a record mixing text, option, number, date, reference and member fields. ReferenceField values must match the target's current targetDisplayField (read it from the target record before writing).",
2673
- command: `zinkee --json records create contacts --set contact-name="Grace Hopper" --set contact-email=grace@example.com --set status=2 --set amount=1999.99 --set due-date=2026-06-01T09:00:00Z --set customer-ref="CUST-12" --set owner=6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d`,
2853
+ description: "Create many records from a JSON array file (one object per record, slug -> value). Best-effort: valid records are created and invalid ones are reported per index in 'errors'. ReferenceField values accept the target record id (a bare UUID, or the compound 'recordId::anything' whose label is ignored) or an exact display value; the server stores the canonical 'recordId::displayValue'.",
2854
+ command: `zinkee --json records create contacts --records-file ./records.json`,
2674
2855
  raw: `{
2675
- "values": {
2676
- "contact-name": "Grace Hopper",
2677
- "contact-email": "grace@example.com",
2678
- "status": 2,
2679
- "amount": 1999.99,
2680
- "due-date": "2026-06-01T09:00:00Z",
2681
- "customer-ref": "CUST-12",
2682
- "owner": "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
2683
- }
2856
+ "records": [
2857
+ {
2858
+ "contact-name": "Ada Lovelace",
2859
+ "contact-email": "ada@example.com"
2860
+ },
2861
+ {
2862
+ "contact-name": "Grace Hopper",
2863
+ "contact-email": "grace@example.com",
2864
+ "status": 2,
2865
+ "amount": 1999.99,
2866
+ "due-date": "2026-06-01T09:00:00Z",
2867
+ "customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621",
2868
+ "owner": "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
2869
+ }
2870
+ ]
2871
+ }`,
2872
+ response: `{
2873
+ "created": [
2874
+ "550e8400-e29b-41d4-a716-446655440000"
2875
+ ],
2876
+ "errors": [
2877
+ {
2878
+ "index": 1,
2879
+ "reason": "record_field_not_found",
2880
+ "message": "Field 'contact-emial' was not found in schema '2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e'.",
2881
+ "action": "Use a valid field slug from the schema and retry."
2882
+ }
2883
+ ]
2884
+ }`
2885
+ },
2886
+ {
2887
+ description: "Set a ReferenceField by the target record id (a bare UUID, or the compound 'recordId::anything' whose label is ignored) or by an exact display value. The server resolves and stores the canonical 'recordId::displayValue'. Unknown references are rejected per index with reason 'data_field_reference_not_found'.",
2888
+ command: `zinkee --json records create deals --set deal-name="Acme renewal" --set customer-ref=00000000-0000-0000-0000-000000000000`,
2889
+ raw: `{
2890
+ "records": [
2891
+ {
2892
+ "deal-name": "Acme renewal",
2893
+ "customer-ref": "00000000-0000-0000-0000-000000000000"
2894
+ }
2895
+ ]
2896
+ }`,
2897
+ response: `{
2898
+ "created": [],
2899
+ "errors": [
2900
+ {
2901
+ "index": 0,
2902
+ "reason": "data_field_reference_not_found",
2903
+ "message": "Referenced record was not found in the target schema.",
2904
+ "action": "Use an existing record id or an existing display value for this reference field."
2905
+ }
2906
+ ]
2684
2907
  }`
2685
2908
  }
2686
2909
  ]),
@@ -2722,6 +2945,22 @@ var specificExamples = {
2722
2945
  command: "zinkee --json records update contacts 550e8400-e29b-41d4-a716-446655440000 --unset contact-email --unset due-date",
2723
2946
  raw: `{
2724
2947
  "unset": ["contact-email", "due-date"]
2948
+ }`
2949
+ },
2950
+ {
2951
+ description: "Set a ReferenceField on an existing record. Pass the target record id (a bare UUID, or the compound 'recordId::anything' whose label is ignored) or an exact display value; the response shows the canonical 'recordId::displayValue' the server stored. If the reference doesn't exist the whole update is rejected with reason 'data_field_reference_not_found'.",
2952
+ command: "zinkee --json records update deals 550e8400-e29b-41d4-a716-446655440000 --set customer-ref=8ad602a1-f6e3-43f6-9ab2-f1c5afc99621",
2953
+ raw: `{
2954
+ "set": {
2955
+ "customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621"
2956
+ }
2957
+ }`,
2958
+ response: `{
2959
+ "recordId": "550e8400-e29b-41d4-a716-446655440000",
2960
+ "values": {
2961
+ "deal-name": "Acme renewal",
2962
+ "customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621::Acme Corp"
2963
+ }
2725
2964
  }`
2726
2965
  }
2727
2966
  ]),
@@ -2863,11 +3102,10 @@ var specificExamples = {
2863
3102
  ]),
2864
3103
  "navigation resources move": buildCommandExamples([
2865
3104
  {
2866
- description: "Move a schema into a navigation folder.",
2867
- command: "zinkee --json navigation resources move 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --type SCHEMA --to Sales",
3105
+ description: "Move a schema into a navigation folder. Only schemas can be moved; the navigation menu holds databases.",
3106
+ command: "zinkee --json navigation resources move 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --to Sales",
2868
3107
  raw: `{
2869
3108
  "resourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
2870
- "resourceType": "SCHEMA",
2871
3109
  "targetFolderId": "84a0fca4-37ef-4c83-a8ed-9ef67ad9324a"
2872
3110
  }`,
2873
3111
  response: `{
@@ -2876,39 +3114,11 @@ var specificExamples = {
2876
3114
  }`
2877
3115
  },
2878
3116
  {
2879
- description: "Move a workflow.",
2880
- command: "zinkee --json navigation resources move 3f8a2c1b-9d4e-4a5b-8c6d-7e8f90123456 --type WORKFLOW --to Operations",
2881
- raw: `{
2882
- "resourceId": "3f8a2c1b-9d4e-4a5b-8c6d-7e8f90123456",
2883
- "resourceType": "WORKFLOW",
2884
- "targetFolderId": "b8c9d0e1-f2a3-4b5c-6d7e-8f9012345678"
2885
- }`
2886
- },
2887
- {
2888
- description: "Move a display.",
2889
- command: "zinkee --json navigation resources move 0f6f062f-1478-4df2-96a1-c495d037d260 --type DISPLAY --to Operations",
2890
- raw: `{
2891
- "resourceId": "0f6f062f-1478-4df2-96a1-c495d037d260",
2892
- "resourceType": "DISPLAY",
2893
- "targetFolderId": "b8c9d0e1-f2a3-4b5c-6d7e-8f9012345678"
2894
- }`
2895
- },
2896
- {
2897
- description: "Move a resource back to the root folder (root is always UUID 00000000-0000-0000-0000-000000000000).",
2898
- command: "zinkee --json navigation resources move 5223cbc8-8c6c-4694-93bc-dd176364df9b --type SCHEMA --to 00000000-0000-0000-0000-000000000000",
3117
+ description: "Move a schema back to the root folder (root is always UUID 00000000-0000-0000-0000-000000000000).",
3118
+ command: "zinkee --json navigation resources move 5223cbc8-8c6c-4694-93bc-dd176364df9b --to 00000000-0000-0000-0000-000000000000",
2899
3119
  raw: `{
2900
3120
  "resourceId": "5223cbc8-8c6c-4694-93bc-dd176364df9b",
2901
- "resourceType": "SCHEMA",
2902
3121
  "targetFolderId": "00000000-0000-0000-0000-000000000000"
2903
- }`
2904
- },
2905
- {
2906
- description: "Move a schedule resource.",
2907
- command: "zinkee --json navigation resources move a1b2c3d4-e5f6-7890-1234-567890abcdef --type SCHEDULE --to Operations",
2908
- raw: `{
2909
- "resourceId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
2910
- "resourceType": "SCHEDULE",
2911
- "targetFolderId": "b8c9d0e1-f2a3-4b5c-6d7e-8f9012345678"
2912
3122
  }`
2913
3123
  }
2914
3124
  ]),
@@ -3306,12 +3516,12 @@ var specificExamples = {
3306
3516
  }`
3307
3517
  },
3308
3518
  {
3309
- description: "Change a DATE variable to use lastWeek.",
3310
- command: "zinkee --json displays variables update 0f6f062f-1478-4df2-96a1-c495d037d260 bbbbbbbb-2222-4222-a222-222222222222 --date-mode dynamic --date-dynamic-option lastWeek",
3519
+ description: "Change a DATE variable to use last12Months.",
3520
+ command: "zinkee --json displays variables update 0f6f062f-1478-4df2-96a1-c495d037d260 bbbbbbbb-2222-4222-a222-222222222222 --date-mode dynamic --date-dynamic-option last12Months",
3311
3521
  raw: `{
3312
3522
  "date": {
3313
3523
  "mode": "dynamic",
3314
- "dynamicOption": "lastWeek"
3524
+ "dynamicOption": "last12Months"
3315
3525
  }
3316
3526
  }`
3317
3527
  }
@@ -3319,7 +3529,7 @@ var specificExamples = {
3319
3529
  "displays widgets create": buildCommandExamples([
3320
3530
  {
3321
3531
  description: "Create a TABLE widget (CLI normalises --type table to TABLE_OR_SUBTABLE).",
3322
- command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type table --name Customers --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":8}' --field id --field contact-name --field status --sort '{"fieldId":"id","direction":"DESC"}'`,
3532
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type table --name Customers --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":8}' --field contact-name --field status --field amount --sort '{"fieldId":"contact-name","direction":"ASC"}'`,
3323
3533
  raw: `{
3324
3534
  "type": "TABLE_OR_SUBTABLE",
3325
3535
  "source": {
@@ -3334,11 +3544,11 @@ var specificExamples = {
3334
3544
  },
3335
3545
  "query": {
3336
3546
  "sorts": [
3337
- { "fieldId": "id", "direction": "DESC" }
3547
+ { "fieldId": "contact-name", "direction": "ASC" }
3338
3548
  ]
3339
3549
  },
3340
3550
  "view": {
3341
- "fields": ["id", "contact-name", "status"]
3551
+ "fields": ["contact-name", "status", "amount"]
3342
3552
  }
3343
3553
  }`,
3344
3554
  response: `{
@@ -3387,8 +3597,8 @@ var specificExamples = {
3387
3597
  }`
3388
3598
  },
3389
3599
  {
3390
- description: "Create a bars CHART widget with a time-serie x-axis and two y-axis series.",
3391
- command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by month" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type bars --x-axis-field due-date --x-axis-date-format MONTH --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"PRIMARY"}' --y-axis-serie '{"typeCalc":"COUNT","fieldCalc":"id","color":"#E57373","axisType":"SECONDARY"}' --legend '{"position":"bottom"}'`,
3600
+ description: "Create a CHART widget with a daily time-serie x-axis.",
3601
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by day" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type lines --x-axis-field due-date --x-axis-date-format DATE --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"PRIMARY"}' --legend '{"position":"bottom"}'`,
3392
3602
  raw: `{
3393
3603
  "type": "CHART",
3394
3604
  "source": {
@@ -3398,11 +3608,11 @@ var specificExamples = {
3398
3608
  "placement": {
3399
3609
  "layout": { "x": 0, "y": 0, "w": 8, "h": 6 }
3400
3610
  },
3401
- "presentation": { "name": "Revenue by month" },
3611
+ "presentation": { "name": "Revenue by day" },
3402
3612
  "chart": {
3403
- "chartType": "bars",
3613
+ "chartType": "lines",
3404
3614
  "xAxisFieldId": "due-date",
3405
- "xAxisDateFormat": "MONTH",
3615
+ "xAxisDateFormat": "DATE",
3406
3616
  "xAxisAsTimeSerie": true,
3407
3617
  "yAxisSeries": [
3408
3618
  {
@@ -3410,12 +3620,6 @@ var specificExamples = {
3410
3620
  "fieldCalc": "amount",
3411
3621
  "color": "#4B9CD3",
3412
3622
  "axisType": "PRIMARY"
3413
- },
3414
- {
3415
- "typeCalc": "COUNT",
3416
- "fieldCalc": "id",
3417
- "color": "#E57373",
3418
- "axisType": "SECONDARY"
3419
3623
  }
3420
3624
  ],
3421
3625
  "legend": { "position": "bottom" }
@@ -3423,71 +3627,246 @@ var specificExamples = {
3423
3627
  }`
3424
3628
  },
3425
3629
  {
3426
- description: "Create a TIMELINE widget.",
3427
- command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type TIMELINE --name "Customer schedule" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":6}' --field contact-name --field due-date`,
3630
+ description: "Create a bars CHART widget with a monthly time-serie x-axis and two y-axis series.",
3631
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by month" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type bars --x-axis-field due-date --x-axis-date-format MONTH_YEAR --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"PRIMARY"}' --y-axis-serie '{"typeCalc":"COUNT","fieldCalc":"contact-name","color":"#E57373","axisType":"SECONDARY"}' --legend '{"position":"bottom"}'`,
3428
3632
  raw: `{
3429
- "type": "TIMELINE",
3633
+ "type": "CHART",
3430
3634
  "source": {
3431
3635
  "originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
3432
3636
  "isOriginSubSchema": false
3433
3637
  },
3434
3638
  "placement": {
3435
- "layout": { "x": 0, "y": 0, "w": 12, "h": 6 }
3639
+ "layout": { "x": 0, "y": 0, "w": 8, "h": 6 }
3436
3640
  },
3437
- "presentation": { "name": "Customer schedule" },
3438
- "view": {
3439
- "fields": ["contact-name", "due-date"]
3641
+ "presentation": { "name": "Revenue by month" },
3642
+ "chart": {
3643
+ "chartType": "bars",
3644
+ "xAxisFieldId": "due-date",
3645
+ "xAxisDateFormat": "MONTH_YEAR",
3646
+ "xAxisAsTimeSerie": true,
3647
+ "yAxisSeries": [
3648
+ {
3649
+ "typeCalc": "SUM",
3650
+ "fieldCalc": "amount",
3651
+ "color": "#4B9CD3",
3652
+ "axisType": "PRIMARY"
3653
+ },
3654
+ {
3655
+ "typeCalc": "COUNT",
3656
+ "fieldCalc": "contact-name",
3657
+ "color": "#E57373",
3658
+ "axisType": "SECONDARY"
3659
+ }
3660
+ ],
3661
+ "legend": { "position": "bottom" }
3440
3662
  }
3441
3663
  }`
3442
3664
  },
3443
3665
  {
3444
- description: "Create a KANBAN widget with a status filter and groupsExpanded config (rich query/view requires --raw).",
3445
- command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --raw '{"type":"KANBAN","source":{"originResourceId":"2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e","isOriginSubSchema":false},"placement":{"layout":{"x":0,"y":0,"w":12,"h":8}},"presentation":{"name":"Status board"},"query":{"filters":[{"origin":{"schemaId":"contacts","fieldId":"status"},"comparisonOperator":"ANY_OF","values":["1","2","3"]}]},"view":{"fields":["contact-name","status","amount"],"groupsExpanded":{"1":true,"2":true,"3":false}}}'`,
3666
+ description: "Create a bars CHART widget with a quarterly time-serie x-axis.",
3667
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by quarter" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type bars --x-axis-field due-date --x-axis-date-format QUARTER_YEAR --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"PRIMARY"}' --legend '{"position":"bottom"}'`,
3446
3668
  raw: `{
3447
- "type": "KANBAN",
3669
+ "type": "CHART",
3448
3670
  "source": {
3449
3671
  "originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
3450
3672
  "isOriginSubSchema": false
3451
3673
  },
3452
3674
  "placement": {
3453
- "layout": { "x": 0, "y": 0, "w": 12, "h": 8 }
3675
+ "layout": { "x": 0, "y": 0, "w": 8, "h": 6 }
3454
3676
  },
3455
- "presentation": { "name": "Status board" },
3456
- "query": {
3457
- "filters": [
3677
+ "presentation": { "name": "Revenue by quarter" },
3678
+ "chart": {
3679
+ "chartType": "bars",
3680
+ "xAxisFieldId": "due-date",
3681
+ "xAxisDateFormat": "QUARTER_YEAR",
3682
+ "xAxisAsTimeSerie": true,
3683
+ "yAxisSeries": [
3458
3684
  {
3459
- "origin": {
3460
- "schemaId": "contacts",
3461
- "fieldId": "status"
3462
- },
3463
- "comparisonOperator": "ANY_OF",
3464
- "values": ["1", "2", "3"]
3685
+ "typeCalc": "SUM",
3686
+ "fieldCalc": "amount",
3687
+ "color": "#4B9CD3",
3688
+ "axisType": "PRIMARY"
3465
3689
  }
3466
- ]
3467
- },
3468
- "view": {
3469
- "fields": ["contact-name", "status", "amount"],
3470
- "groupsExpanded": { "1": true, "2": true, "3": false }
3690
+ ],
3691
+ "legend": { "position": "bottom" }
3471
3692
  }
3472
3693
  }`
3473
3694
  },
3474
3695
  {
3475
- description: "Create a HIERARCHY widget rooted on a parent field.",
3476
- command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --raw '{"type":"HIERARCHY","source":{"originResourceId":"2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e","isOriginSubSchema":false},"placement":{"layout":{"x":0,"y":0,"w":12,"h":6}},"presentation":{"name":"Org tree"},"view":{"fields":["contact-name","status"],"hierarchyConfig":{"parentFieldId":"contact-name"}}}'`,
3696
+ description: "Create a CHART widget with a yearly time-serie x-axis.",
3697
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by year" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type lines --x-axis-field due-date --x-axis-date-format YEAR --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"PRIMARY"}' --legend '{"position":"bottom"}'`,
3477
3698
  raw: `{
3478
- "type": "HIERARCHY",
3699
+ "type": "CHART",
3479
3700
  "source": {
3480
3701
  "originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
3481
3702
  "isOriginSubSchema": false
3482
3703
  },
3483
3704
  "placement": {
3484
- "layout": { "x": 0, "y": 0, "w": 12, "h": 6 }
3705
+ "layout": { "x": 0, "y": 0, "w": 8, "h": 6 }
3706
+ },
3707
+ "presentation": { "name": "Revenue by year" },
3708
+ "chart": {
3709
+ "chartType": "lines",
3710
+ "xAxisFieldId": "due-date",
3711
+ "xAxisDateFormat": "YEAR",
3712
+ "xAxisAsTimeSerie": true,
3713
+ "yAxisSeries": [
3714
+ {
3715
+ "typeCalc": "SUM",
3716
+ "fieldCalc": "amount",
3717
+ "color": "#4B9CD3",
3718
+ "axisType": "PRIMARY"
3719
+ }
3720
+ ],
3721
+ "legend": { "position": "bottom" }
3722
+ }
3723
+ }`
3724
+ },
3725
+ {
3726
+ description: "Create a TIMELINE widget via flags. The typed view state lives in view.viewAdditionalProperties.viewState; field references there must be UUIDs (slugs are only resolved in --field). cardConfig uses category1/category2/category4 (no category3). Use --view-state for UI scalars (granularity, sidebarWidth, show* ...).",
3727
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type TIMELINE --name "Customer schedule" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":6}' --field c60b8526-5835-4c9f-aecd-9a16c7704a34 --field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --field d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90 --start-date-field d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90 --end-date-field e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9012 --label-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --group-field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --subgroup-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --progress-field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --card-title-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --card-category1-field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --card-category2-field d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90 --card-category4-field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --card-description-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --view-state '{"granularity":"month","sidebarWidth":300,"showUngroupedEvents":true,"showUnsubgroupedEvents":true}'`,
3728
+ raw: `{
3729
+ "type": "TIMELINE",
3730
+ "source": {
3731
+ "originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
3732
+ "isOriginSubSchema": false
3733
+ },
3734
+ "placement": {
3735
+ "layout": { "x": 0, "y": 0, "w": 12, "h": 6 }
3736
+ },
3737
+ "presentation": { "name": "Customer schedule" },
3738
+ "view": {
3739
+ "fields": [
3740
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3741
+ "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3742
+ "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
3743
+ "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90"
3744
+ ],
3745
+ "viewAdditionalProperties": {
3746
+ "viewState": {
3747
+ "granularity": "month",
3748
+ "sidebarWidth": 300,
3749
+ "showUngroupedEvents": true,
3750
+ "showUnsubgroupedEvents": true,
3751
+ "fieldMapping": {
3752
+ "startDateField": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
3753
+ "endDateField": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9012",
3754
+ "labelField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3755
+ "groupField": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3756
+ "subgroupField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3757
+ "progressField": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
3758
+ "cardConfig": {
3759
+ "titleField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3760
+ "category1Field": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3761
+ "category2Field": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
3762
+ "category4Field": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
3763
+ "descriptionField": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
3764
+ }
3765
+ }
3766
+ }
3767
+ }
3768
+ }
3769
+ }`
3770
+ },
3771
+ {
3772
+ description: "Create a KANBAN widget via flags. The typed view state lives in view.viewAdditionalProperties.viewState; field references there must be UUIDs (slugs are only resolved in --field). cardConfig uses category1/category2/category3 (no category4). --aggregation-method/--aggregation-field set the card aggregation; use --view-state for UI scalars.",
3773
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type KANBAN --name "Status board" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":8}' --field c60b8526-5835-4c9f-aecd-9a16c7704a34 --field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --field d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90 --column-field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --swimlane-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --card-title-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --card-category1-field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --card-category2-field d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90 --card-category3-field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --card-description-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --aggregation-method sum --aggregation-field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --view-state '{"hideEmptyColumns":false,"showNoValueColumn":true,"showNoValueSwimlane":true}'`,
3774
+ raw: `{
3775
+ "type": "KANBAN",
3776
+ "source": {
3777
+ "originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
3778
+ "isOriginSubSchema": false
3779
+ },
3780
+ "placement": {
3781
+ "layout": { "x": 0, "y": 0, "w": 12, "h": 8 }
3782
+ },
3783
+ "presentation": { "name": "Status board" },
3784
+ "view": {
3785
+ "fields": [
3786
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3787
+ "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3788
+ "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
3789
+ "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90"
3790
+ ],
3791
+ "viewAdditionalProperties": {
3792
+ "viewState": {
3793
+ "hideEmptyColumns": false,
3794
+ "showNoValueColumn": true,
3795
+ "showNoValueSwimlane": true,
3796
+ "fieldMapping": {
3797
+ "columnField": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3798
+ "swimlaneField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3799
+ "cardConfig": {
3800
+ "titleField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3801
+ "category1Field": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3802
+ "category2Field": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
3803
+ "category3Field": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
3804
+ "descriptionField": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
3805
+ },
3806
+ "aggregationConfig": {
3807
+ "method": "sum",
3808
+ "fieldId": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043"
3809
+ }
3810
+ }
3811
+ }
3812
+ }
3813
+ }
3814
+ }`
3815
+ },
3816
+ {
3817
+ description: "Create a HIERARCHY widget via flags. Declare columns once (--hierarchy-label, repeatable --hierarchy-info <id:name>, --hierarchy-number <id:name:aggregation>), map the root level (the view's own table) with --hierarchy-root-* and add each child level as a JSON object with --hierarchy-child. label is required per level; info/number mappings are optional. Column ids (e.g. col_1779975207355_zfndknslw) are unique opaque identifiers you choose \u2014 not the column name \u2014 and are reused as columnId in every mapping. All field references must be UUIDs.",
3818
+ command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type HIERARCHY --name "Org tree" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":6}' --field c60b8526-5835-4c9f-aecd-9a16c7704a34 --field a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890 --field c52efe3f-2825-4c60-a7e3-34dd4c1ce043 --hierarchy-label "Name" --hierarchy-info "col_1779975207355_zfndknslw:Status" --hierarchy-info "col_1779975243018_qm4t7xb2pa:Description" --hierarchy-number "col_1779975261490_h8kd3nv9ws:Total amount:SUM" --hierarchy-root-name "Customers" --hierarchy-root-label-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --hierarchy-root-info "col_1779975207355_zfndknslw:a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890" --hierarchy-root-number "col_1779975261490_h8kd3nv9ws:c52efe3f-2825-4c60-a7e3-34dd4c1ce043" --hierarchy-child '{"name":"Projects","schemaId":"3ec9cdef-7e0a-4cdd-8019-8fb9281f5d6f","childFieldId":"80deefae-af3d-4000-b322-bfe1594f8073","labelFieldId":"50dbef0b-7e0a-4fdd-8019-8fb9281f5d70","infoMappings":[{"columnId":"col_1779975243018_qm4t7xb2pa","fieldId":"60dcef0c-8f1b-4eee-9120-9fc0392f6e71"}],"numberMappings":[{"columnId":"col_1779975261490_h8kd3nv9ws","fieldId":"70ddef0d-9f2c-4fff-a221-afd0493f7f72"}]}'`,
3819
+ raw: `{
3820
+ "type": "HIERARCHY",
3821
+ "source": {
3822
+ "originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
3823
+ "isOriginSubSchema": false
3824
+ },
3825
+ "placement": {
3826
+ "layout": { "x": 0, "y": 0, "w": 12, "h": 6 }
3485
3827
  },
3486
3828
  "presentation": { "name": "Org tree" },
3487
3829
  "view": {
3488
- "fields": ["contact-name", "status"],
3830
+ "fields": [
3831
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3832
+ "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
3833
+ "c52efe3f-2825-4c60-a7e3-34dd4c1ce043"
3834
+ ],
3489
3835
  "hierarchyConfig": {
3490
- "parentFieldId": "contact-name"
3836
+ "columns": {
3837
+ "label": "Name",
3838
+ "infos": [
3839
+ { "id": "col_1779975207355_zfndknslw", "name": "Status" },
3840
+ { "id": "col_1779975243018_qm4t7xb2pa", "name": "Description" }
3841
+ ],
3842
+ "numbers": [
3843
+ { "id": "col_1779975261490_h8kd3nv9ws", "name": "Total amount", "aggregation": "SUM" }
3844
+ ]
3845
+ },
3846
+ "rootLevel": {
3847
+ "name": "Customers",
3848
+ "labelFieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3849
+ "infoMappings": [
3850
+ { "columnId": "col_1779975207355_zfndknslw", "fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890" }
3851
+ ],
3852
+ "numberMappings": [
3853
+ { "columnId": "col_1779975261490_h8kd3nv9ws", "fieldId": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043" }
3854
+ ]
3855
+ },
3856
+ "childLevels": [
3857
+ {
3858
+ "name": "Projects",
3859
+ "schemaId": "3ec9cdef-7e0a-4cdd-8019-8fb9281f5d6f",
3860
+ "childFieldId": "80deefae-af3d-4000-b322-bfe1594f8073",
3861
+ "labelFieldId": "50dbef0b-7e0a-4fdd-8019-8fb9281f5d70",
3862
+ "infoMappings": [
3863
+ { "columnId": "col_1779975243018_qm4t7xb2pa", "fieldId": "60dcef0c-8f1b-4eee-9120-9fc0392f6e71" }
3864
+ ],
3865
+ "numberMappings": [
3866
+ { "columnId": "col_1779975261490_h8kd3nv9ws", "fieldId": "70ddef0d-9f2c-4fff-a221-afd0493f7f72" }
3867
+ ]
3868
+ }
3869
+ ]
3491
3870
  }
3492
3871
  }
3493
3872
  }`
@@ -3496,13 +3875,13 @@ var specificExamples = {
3496
3875
  "displays widgets update": buildCommandExamples([
3497
3876
  {
3498
3877
  description: "Move placement, update view columns, rename the widget and lock it.",
3499
- command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --layout '{"x":1,"y":2,"w":8,"h":4}' --field id --field contact-name --field status --name "Revenue Updated" --widget-read-only`,
3878
+ command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --layout '{"x":1,"y":2,"w":8,"h":4}' --field contact-name --field status --field amount --name "Revenue Updated" --widget-read-only`,
3500
3879
  raw: `{
3501
3880
  "placement": {
3502
3881
  "layout": { "x": 1, "y": 2, "w": 8, "h": 4 }
3503
3882
  },
3504
3883
  "view": {
3505
- "fields": ["id", "contact-name", "status"]
3884
+ "fields": ["contact-name", "status", "amount"]
3506
3885
  },
3507
3886
  "presentation": { "name": "Revenue Updated" },
3508
3887
  "behavior": { "readOnly": true }
@@ -3513,29 +3892,38 @@ var specificExamples = {
3513
3892
  }`
3514
3893
  },
3515
3894
  {
3516
- description: "Add query filters that match the full comparisonOperator contract (requires --raw because --filter only accepts free-form expressions).",
3517
- command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --raw '{"query":{"filters":[{"origin":{"schemaId":"contacts","fieldId":"status"},"comparisonOperator":"EQUAL","values":["2"]},{"origin":{"schemaId":"contacts","fieldId":"amount"},"comparisonOperator":"GREATER_OR_EQUAL","values":["500"]}]}}'`,
3895
+ description: "Add query filters with --filter. The CLI parses the expression and emits the structured comparisonOperator payload the API expects.",
3896
+ command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --filter "status:eq:2" --filter "amount:gte:500"`,
3518
3897
  raw: `{
3519
3898
  "query": {
3520
3899
  "filters": [
3521
3900
  {
3522
- "origin": {
3523
- "schemaId": "contacts",
3524
- "fieldId": "status"
3525
- },
3901
+ "origin": { "fieldId": "status" },
3526
3902
  "comparisonOperator": "EQUAL",
3527
3903
  "values": ["2"]
3528
3904
  },
3529
3905
  {
3530
- "origin": {
3531
- "schemaId": "contacts",
3532
- "fieldId": "amount"
3533
- },
3906
+ "origin": { "fieldId": "amount" },
3534
3907
  "comparisonOperator": "GREATER_OR_EQUAL",
3535
3908
  "values": ["500"]
3536
3909
  }
3537
3910
  ]
3538
3911
  }
3912
+ }`
3913
+ },
3914
+ {
3915
+ description: "Fall back to --raw for comparators the parser does not cover yet (e.g. NOT_CONTAINS, RANGE).",
3916
+ command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --raw '{"query":{"filters":[{"origin":{"fieldId":"due-date"},"comparisonOperator":"RANGE","values":["2025-01-01","2025-12-31"]}]}}'`,
3917
+ raw: `{
3918
+ "query": {
3919
+ "filters": [
3920
+ {
3921
+ "origin": { "fieldId": "due-date" },
3922
+ "comparisonOperator": "RANGE",
3923
+ "values": ["2025-01-01", "2025-12-31"]
3924
+ }
3925
+ ]
3926
+ }
3539
3927
  }`
3540
3928
  },
3541
3929
  {
@@ -3561,13 +3949,13 @@ var specificExamples = {
3561
3949
  }`
3562
3950
  },
3563
3951
  {
3564
- description: "Replace the chart series with weekly lines.",
3565
- command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 22222222-2222-4222-a222-222222222222 --chart-type lines --x-axis-field due-date --x-axis-date-format WEEK --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#2E7D32","axisType":"PRIMARY"}'`,
3952
+ description: "Replace the chart series with quarterly lines.",
3953
+ command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 22222222-2222-4222-a222-222222222222 --chart-type lines --x-axis-field due-date --x-axis-date-format QUARTER_YEAR --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#2E7D32","axisType":"PRIMARY"}'`,
3566
3954
  raw: `{
3567
3955
  "chart": {
3568
3956
  "chartType": "lines",
3569
3957
  "xAxisFieldId": "due-date",
3570
- "xAxisDateFormat": "WEEK",
3958
+ "xAxisDateFormat": "QUARTER_YEAR",
3571
3959
  "xAxisAsTimeSerie": true,
3572
3960
  "yAxisSeries": [
3573
3961
  {
@@ -3733,6 +4121,7 @@ var specificExamples = {
3733
4121
  },
3734
4122
  "auditable": true,
3735
4123
  "commentable": true,
4124
+ "highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
3736
4125
  "fields": [
3737
4126
  {
3738
4127
  "id": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
@@ -3922,7 +4311,7 @@ var specificExamples = {
3922
4311
  "required": false,
3923
4312
  "system": false,
3924
4313
  "config": {
3925
- "formulaText": "date_format(d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90, "AAAA-MM")",
4314
+ "formulaText": "date_format(d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90, \\"AAAA-MM\\")",
3926
4315
  "uiFormatKind": "date",
3927
4316
  "status": "ACTIVE",
3928
4317
  "defaultValue": null,
@@ -4041,6 +4430,10 @@ var specificExamples = {
4041
4430
  "conditionalFormatting": []
4042
4431
  }
4043
4432
  }
4433
+ ],
4434
+ "uniqueFields": [
4435
+ "c60b8526-5835-4c9f-aecd-9a16c7704a34",
4436
+ "1d2e3f4a-5b6c-7d8e-9f01-23456789abcd"
4044
4437
  ]
4045
4438
  }`
4046
4439
  }
@@ -4199,10 +4592,24 @@ var specificExamples = {
4199
4592
  ]),
4200
4593
  "records delete": buildCommandExamples([
4201
4594
  {
4202
- description: "Delete a record.",
4203
- command: `zinkee --json records delete contacts ${SAMPLE_UUID}`,
4595
+ description: "Delete one or more records by id (--record is repeatable).",
4596
+ command: `zinkee --json records delete contacts --record 550e8400-e29b-41d4-a716-446655440000 --record 6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d`,
4204
4597
  response: `{
4205
- "recordId": "550e8400-e29b-41d4-a716-446655440000",
4598
+ "recordIds": [
4599
+ "550e8400-e29b-41d4-a716-446655440000",
4600
+ "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
4601
+ ],
4602
+ "deleted": true
4603
+ }`
4604
+ },
4605
+ {
4606
+ description: "Delete many records listing one uuid per line in a file (merged with any --record flags and de-duplicated).",
4607
+ command: `zinkee --json records delete contacts --records-file ./record-ids.txt`,
4608
+ response: `{
4609
+ "recordIds": [
4610
+ "550e8400-e29b-41d4-a716-446655440000",
4611
+ "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
4612
+ ],
4206
4613
  "deleted": true
4207
4614
  }`
4208
4615
  }
@@ -4298,7 +4705,7 @@ var specificExamples = {
4298
4705
  "resources": [
4299
4706
  {
4300
4707
  "id": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
4301
- "name": "Contacts",
4708
+ "name": "Customer records",
4302
4709
  "type": "SCHEMA",
4303
4710
  "isInSpace": false,
4304
4711
  "additionalProperties": {}
@@ -4322,19 +4729,10 @@ var specificExamples = {
4322
4729
  "resources": [
4323
4730
  {
4324
4731
  "id": "3f8a2c1b-9d4e-4a5b-8c6d-7e8f90123456",
4325
- "name": "Quote approval",
4326
- "type": "WORKFLOW",
4732
+ "name": "Shipments",
4733
+ "type": "SCHEMA",
4327
4734
  "isInSpace": false,
4328
4735
  "additionalProperties": {}
4329
- },
4330
- {
4331
- "id": "0f6f062f-1478-4df2-96a1-c495d037d260",
4332
- "name": "Executive Revenue",
4333
- "type": "DISPLAY",
4334
- "isInSpace": true,
4335
- "additionalProperties": {
4336
- "pinned": true
4337
- }
4338
4736
  }
4339
4737
  ],
4340
4738
  "additionalProperties": {
@@ -4381,8 +4779,13 @@ var specificExamples = {
4381
4779
  "name": "Shared Dashboards",
4382
4780
  "order": 1,
4383
4781
  "isRoot": false,
4384
- "resourceCount": 1,
4782
+ "resourceCount": 2,
4385
4783
  "resources": [
4784
+ {
4785
+ "id": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
4786
+ "name": "Customer records",
4787
+ "type": "SCHEMA"
4788
+ },
4386
4789
  {
4387
4790
  "id": "0f6f062f-1478-4df2-96a1-c495d037d260",
4388
4791
  "name": "Executive Revenue",
@@ -4554,7 +4957,7 @@ var specificExamples = {
4554
4957
  "hideDeleteButton": false
4555
4958
  },
4556
4959
  "view": {
4557
- "fields": ["id", "contact-name", "status", "amount"],
4960
+ "fields": ["contact-name", "status", "amount"],
4558
4961
  "columnSize": { "contact-name": 240 }
4559
4962
  }
4560
4963
  }
@@ -4601,6 +5004,83 @@ var specificExamples = {
4601
5004
  command: "zinkee --json displays delete 0f6f062f-1478-4df2-96a1-c495d037d260"
4602
5005
  }
4603
5006
  ]),
5007
+ "displays folders list": buildCommandExamples([
5008
+ {
5009
+ description: "List flat display folders (non-root folders plus the root bucket).",
5010
+ command: "zinkee --json displays folders list",
5011
+ response: `[
5012
+ {
5013
+ "id": "e7a1c3d2-5f4b-4a6c-9d8e-1f2a3b4c5d6e",
5014
+ "name": "Executive Dashboards",
5015
+ "isRoot": false,
5016
+ "displayCount": 2
5017
+ },
5018
+ {
5019
+ "id": "8b22282b-3db3-4dd7-87f7-b29365dcf3f6",
5020
+ "name": null,
5021
+ "isRoot": true,
5022
+ "displayCount": 4
5023
+ }
5024
+ ]`
5025
+ }
5026
+ ]),
5027
+ "displays folders create": buildCommandExamples([
5028
+ {
5029
+ description: "Create one flat display folder.",
5030
+ command: 'zinkee --json displays folders create --name "Executive Dashboards"',
5031
+ raw: `{
5032
+ "name": "Executive Dashboards"
5033
+ }`,
5034
+ response: `{
5035
+ "id": "e7a1c3d2-5f4b-4a6c-9d8e-1f2a3b4c5d6e",
5036
+ "name": "Executive Dashboards",
5037
+ "isRoot": false,
5038
+ "displayCount": 0
5039
+ }`
5040
+ }
5041
+ ]),
5042
+ "displays folders update": buildCommandExamples([
5043
+ {
5044
+ description: "Rename a display folder. The folder argument accepts a UUID or an exact name.",
5045
+ command: 'zinkee --json displays folders update "Executive Dashboards" --name "Executive Dashboards (2026)"',
5046
+ raw: `{
5047
+ "name": "Executive Dashboards (2026)"
5048
+ }`,
5049
+ response: `{
5050
+ "id": "e7a1c3d2-5f4b-4a6c-9d8e-1f2a3b4c5d6e",
5051
+ "name": "Executive Dashboards (2026)",
5052
+ "isRoot": false,
5053
+ "displayCount": 2
5054
+ }`
5055
+ }
5056
+ ]),
5057
+ "displays folders delete": buildCommandExamples([
5058
+ {
5059
+ description: "Delete a non-root display folder. Its displays move back to the root folder.",
5060
+ command: 'zinkee --json displays folders delete "Executive Dashboards"',
5061
+ response: `{
5062
+ "id": "e7a1c3d2-5f4b-4a6c-9d8e-1f2a3b4c5d6e",
5063
+ "name": "Executive Dashboards",
5064
+ "isRoot": false,
5065
+ "displayCount": 2
5066
+ }`
5067
+ }
5068
+ ]),
5069
+ "displays folders move": buildCommandExamples([
5070
+ {
5071
+ description: "Move one display into a target folder by exact folder name.",
5072
+ command: 'zinkee --json displays folders move 0f6f062f-1478-4df2-96a1-c495d037d260 --to "Executive Dashboards"',
5073
+ raw: `{
5074
+ "displayId": "0f6f062f-1478-4df2-96a1-c495d037d260",
5075
+ "targetFolderId": "e7a1c3d2-5f4b-4a6c-9d8e-1f2a3b4c5d6e"
5076
+ }`,
5077
+ response: `{
5078
+ "id": "e7a1c3d2-5f4b-4a6c-9d8e-1f2a3b4c5d6e",
5079
+ "name": "Executive Dashboards",
5080
+ "isRoot": false
5081
+ }`
5082
+ }
5083
+ ]),
4604
5084
  "displays layout get": buildCommandExamples([
4605
5085
  {
4606
5086
  description: "Get the layout block of a display (template + tabs/freeformLayouts/slots).",
@@ -4694,7 +5174,7 @@ var specificExamples = {
4694
5174
  "hideDeleteButton": false
4695
5175
  },
4696
5176
  "view": {
4697
- "fields": ["id", "contact-name", "status", "amount"],
5177
+ "fields": ["contact-name", "status", "amount"],
4698
5178
  "columnSize": { "contact-name": 240 }
4699
5179
  }
4700
5180
  }`
@@ -5351,6 +5831,43 @@ var specificExamples = {
5351
5831
  "steps": ["one", "two"]
5352
5832
  }
5353
5833
  }
5834
+ }`
5835
+ },
5836
+ {
5837
+ description: "Add a send-message action. --to maps to fieldMapping.recipients and --message maps to fieldMapping.body. Values are wrapped in {rawValue, source} like every other automation field mapping; use --map / --map-json for extra keys.",
5838
+ command: `zinkee --json automations actions add automation-1 --type send_message --name "Notify customer" --to customer@example.com --subject "Your order status has changed" --message "Hello, your order is now processed."`,
5839
+ raw: `{
5840
+ "type": "send_message",
5841
+ "name": "Notify customer",
5842
+ "config": {
5843
+ "fieldMapping": {
5844
+ "recipients": {
5845
+ "rawValue": "customer@example.com",
5846
+ "source": null
5847
+ },
5848
+ "subject": {
5849
+ "rawValue": "Your order status has changed",
5850
+ "source": null
5851
+ },
5852
+ "body": {
5853
+ "rawValue": "Hello, your order is now processed.",
5854
+ "source": null
5855
+ }
5856
+ }
5857
+ }
5858
+ }`
5859
+ },
5860
+ {
5861
+ description: "Add a generate-document action. The renderer reads the template, writes the file, and stores its id in the target FileField of the target schema.",
5862
+ command: 'zinkee --json automations actions add automation-1 --type generate_document --name "Generate invoice PDF" --document-template 6c08673e-f8a6-44d2-8c91-d09eca911f8f --target-schema 68d2c7be-4392-42de-b5ae-625ad79cfc3c --target-file-field c8edbde8-a361-4e9f-a39a-2e88971f21a7',
5863
+ raw: `{
5864
+ "type": "generate_document",
5865
+ "name": "Generate invoice PDF",
5866
+ "config": {
5867
+ "documentTemplateId": "6c08673e-f8a6-44d2-8c91-d09eca911f8f",
5868
+ "targetSchemaId": "68d2c7be-4392-42de-b5ae-625ad79cfc3c",
5869
+ "targetFileFieldId": "c8edbde8-a361-4e9f-a39a-2e88971f21a7"
5870
+ }
5354
5871
  }`
5355
5872
  }
5356
5873
  ]),
@@ -5401,6 +5918,30 @@ var specificExamples = {
5401
5918
  "authMode": "STORED",
5402
5919
  "connectionId": "connection-1"
5403
5920
  }
5921
+ }`
5922
+ },
5923
+ {
5924
+ description: "Replace a send-message action. --to maps to fieldMapping.recipients and --message maps to fieldMapping.body; values are wrapped in {rawValue, source}.",
5925
+ command: `zinkee --json automations actions update automation-1 action-1 --type send_message --name "Notify customer" --to customer@example.com --subject "Your order status has changed" --message "Hello, your order is now processed."`,
5926
+ raw: `{
5927
+ "type": "send_message",
5928
+ "name": "Notify customer",
5929
+ "config": {
5930
+ "fieldMapping": {
5931
+ "recipients": {
5932
+ "rawValue": "customer@example.com",
5933
+ "source": null
5934
+ },
5935
+ "subject": {
5936
+ "rawValue": "Your order status has changed",
5937
+ "source": null
5938
+ },
5939
+ "body": {
5940
+ "rawValue": "Hello, your order is now processed.",
5941
+ "source": null
5942
+ }
5943
+ }
5944
+ }
5404
5945
  }`
5405
5946
  }
5406
5947
  ]),
@@ -5553,7 +6094,8 @@ var specificExamples = {
5553
6094
  description: "Move one automation into a target folder by exact folder name.",
5554
6095
  command: 'zinkee --json automations folders move automation-1 --to "Codex Frontend Demo"',
5555
6096
  raw: `{
5556
- "automationId": "automation-1"
6097
+ "automationId": "automation-1",
6098
+ "targetFolderId": "cdfc14e3-3169-4ffb-8d9b-b11858a9240f"
5557
6099
  }`,
5558
6100
  response: `{
5559
6101
  "id": "cdfc14e3-3169-4ffb-8d9b-b11858a9240f",
@@ -5561,70 +6103,248 @@ var specificExamples = {
5561
6103
  "isRoot": false
5562
6104
  }`
5563
6105
  }
5564
- ])
5565
- };
5566
- function fallbackExamples(commandName) {
5567
- switch (commandName) {
5568
- default:
5569
- return [];
5570
- }
5571
- }
5572
- function buildSchemaFieldCreateExamples(type) {
5573
- const definitions = getSchemaFieldTypeDefinitions();
5574
- const selectedDefinition = resolveSchemaFieldType(type);
5575
- const filteredDefinitions = selectedDefinition ? definitions.filter((definition) => definition.key === selectedDefinition.key) : definitions;
5576
- const flat = filteredDefinitions.flatMap(
5577
- (definition) => definition.examplePayloads.map((example) => ({ definition, example }))
5578
- );
5579
- return buildCommandExamples(
5580
- flat.map(({ example }, index) => {
5581
- const slug = typeof example.payload.slug === "string" ? example.payload.slug : "field";
5582
- const command = `zinkee --json schemas fields create contacts --raw '${JSON.stringify(example.payload)}'`;
5583
- if (index === 0) {
5584
- return {
5585
- description: example.name,
5586
- command,
5587
- response: `{
5588
- "schemaId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
5589
- "fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
5590
- "slug": "${slug}"
5591
- }`
5592
- };
6106
+ ]),
6107
+ "document-templates list": buildCommandExamples([
6108
+ {
6109
+ description: "List every document template in the workspace. Each asset's link is a relative path to the asset download endpoint, ready to call without composition.",
6110
+ command: "zinkee --json document-templates list",
6111
+ response: `[
6112
+ {
6113
+ "id": "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6114
+ "name": "Quarterly report",
6115
+ "assets": [
6116
+ {
6117
+ "id": "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6118
+ "name": "Requirements.docx",
6119
+ "link": "/api/v2/document-templates/7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e/assets/9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f/download"
5593
6120
  }
5594
- return {
5595
- description: example.name,
5596
- command
5597
- };
5598
- })
5599
- );
5600
- }
5601
- function getCommandExamples(commandName, filters = {}) {
5602
- if (commandName === "schemas fields create") {
5603
- return buildSchemaFieldCreateExamples(filters.type);
6121
+ ],
6122
+ "createdAt": "2026-01-15T10:00:00Z",
6123
+ "updatedAt": "2026-01-20T14:30:00Z",
6124
+ "createdBy": "creator@example.com",
6125
+ "updatedBy": "updater@example.com"
5604
6126
  }
5605
- return specificExamples[commandName] ?? fallbackExamples(commandName);
5606
- }
5607
- function getResolvedCommandExamples(commandName, filters = {}) {
5608
- return getCommandExamples(commandName, filters);
5609
- }
5610
-
5611
- // src/cli-examples.ts
5612
- function getStdout(stdout) {
5613
- return stdout ?? process.stdout;
5614
- }
5615
- function extractExampleFilters(tokens) {
5616
- const filters = {};
5617
- for (let index = 0; index < tokens.length; index += 1) {
5618
- const token = tokens[index];
5619
- if (token === "--type") {
5620
- const value = tokens[index + 1];
5621
- if (value && !value.startsWith("-")) {
5622
- filters.type = value;
5623
- index += 1;
5624
- }
6127
+ ]`
5625
6128
  }
5626
- }
5627
- return filters;
6129
+ ]),
6130
+ "document-templates get": buildCommandExamples([
6131
+ {
6132
+ description: "Look up one template by UUID. The CLI filters the list endpoint locally (the backend does not expose a get-one route).",
6133
+ command: "zinkee --json document-templates get 7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6134
+ response: `{
6135
+ "id": "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6136
+ "name": "Quarterly report",
6137
+ "assets": [
6138
+ {
6139
+ "id": "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6140
+ "name": "Requirements.docx",
6141
+ "link": "/api/v2/document-templates/7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e/assets/9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f/download"
6142
+ }
6143
+ ],
6144
+ "createdAt": "2026-01-15T10:00:00Z",
6145
+ "updatedAt": "2026-01-20T14:30:00Z",
6146
+ "createdBy": "creator@example.com",
6147
+ "updatedBy": "creator@example.com"
6148
+ }`
6149
+ },
6150
+ {
6151
+ description: "Look up by exact template name. Names are unique within the workspace; ambiguity is reported as an error.",
6152
+ command: 'zinkee --json document-templates get "Quarterly report"'
6153
+ }
6154
+ ]),
6155
+ "document-templates create": buildCommandExamples([
6156
+ {
6157
+ description: "Typical create: empty template by name. Assets are uploaded afterwards via document-templates assets add.",
6158
+ command: 'zinkee --json document-templates create --name "Quarterly report"',
6159
+ raw: `{
6160
+ "name": "Quarterly report",
6161
+ "assets": []
6162
+ }`,
6163
+ response: `{
6164
+ "id": "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6165
+ "name": "Quarterly report",
6166
+ "assets": [],
6167
+ "createdAt": "2026-01-15T10:00:00Z",
6168
+ "updatedAt": "2026-01-15T10:00:00Z",
6169
+ "createdBy": "creator@example.com",
6170
+ "updatedBy": "creator@example.com"
6171
+ }`
6172
+ },
6173
+ {
6174
+ description: "Create with pre-existing asset records via --raw. Used for import/migration flows where the assets already exist elsewhere; the typical UX is to create empty and upload via assets add.",
6175
+ command: `zinkee --json document-templates create --raw '{"name":"Annual report","assets":[{"id":"9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f","name":"Cover.docx","link":"/existing/cover.docx"},{"id":"3f2e1d0c-9b8a-7654-3210-fedcba987654","name":"Appendix.docx","link":"/existing/appendix.docx"}]}'`,
6176
+ raw: `{
6177
+ "name": "Annual report",
6178
+ "assets": [
6179
+ {
6180
+ "id": "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6181
+ "name": "Cover.docx",
6182
+ "link": "/existing/cover.docx"
6183
+ },
6184
+ {
6185
+ "id": "3f2e1d0c-9b8a-7654-3210-fedcba987654",
6186
+ "name": "Appendix.docx",
6187
+ "link": "/existing/appendix.docx"
6188
+ }
6189
+ ]
6190
+ }`
6191
+ }
6192
+ ]),
6193
+ "document-templates update": buildCommandExamples([
6194
+ {
6195
+ description: "Rename a template. The template argument accepts a UUID or an exact current name. Only the name is updatable through this endpoint; assets are managed via the assets subcommands.",
6196
+ command: 'zinkee --json document-templates update "Quarterly report" --name "Annual report"',
6197
+ raw: `{
6198
+ "name": "Annual report"
6199
+ }`,
6200
+ response: `{
6201
+ "id": "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6202
+ "name": "Annual report",
6203
+ "assets": [
6204
+ {
6205
+ "id": "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6206
+ "name": "Requirements.docx",
6207
+ "link": "/api/v2/document-templates/7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e/assets/9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f/download"
6208
+ }
6209
+ ],
6210
+ "createdAt": "2026-01-15T10:00:00Z",
6211
+ "updatedAt": "2026-01-22T09:15:00Z",
6212
+ "createdBy": "creator@example.com",
6213
+ "updatedBy": "updater@example.com"
6214
+ }`
6215
+ }
6216
+ ]),
6217
+ "document-templates delete": buildCommandExamples([
6218
+ {
6219
+ description: "Delete a template and remove its asset files from storage. Record fields that reference previously generated documents are not modified.",
6220
+ command: "zinkee --json document-templates delete 7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6221
+ response: `{
6222
+ "id": "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6223
+ "deleted": true
6224
+ }`
6225
+ }
6226
+ ]),
6227
+ "document-templates assets add": buildCommandExamples([
6228
+ {
6229
+ description: 'Upload a local file as an asset of a template. The CLI sends a multipart form with a part named "file"; the asset display name defaults to the uploaded filename.',
6230
+ command: 'zinkee --json document-templates assets add "Quarterly report" ./contract.docx',
6231
+ response: `{
6232
+ "id": "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e",
6233
+ "name": "Quarterly report",
6234
+ "assets": [
6235
+ {
6236
+ "id": "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6237
+ "name": "contract.docx",
6238
+ "link": "/api/v2/document-templates/7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e/assets/9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f/download"
6239
+ }
6240
+ ],
6241
+ "createdAt": "2026-01-15T10:00:00Z",
6242
+ "updatedAt": "2026-01-22T09:15:00Z",
6243
+ "createdBy": "creator@example.com",
6244
+ "updatedBy": "updater@example.com"
6245
+ }`
6246
+ },
6247
+ {
6248
+ description: "Override the asset display name with --name (otherwise the uploaded filename is used).",
6249
+ command: 'zinkee --json document-templates assets add "Quarterly report" ./contract.docx --name "Customer Contract"'
6250
+ }
6251
+ ]),
6252
+ "document-templates assets delete": buildCommandExamples([
6253
+ {
6254
+ description: "Delete one asset from a template and remove its file from storage.",
6255
+ command: "zinkee --json document-templates assets delete 7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e 9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6256
+ response: `{
6257
+ "id": "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
6258
+ "deleted": true
6259
+ }`
6260
+ }
6261
+ ]),
6262
+ "document-templates assets download": buildCommandExamples([
6263
+ {
6264
+ description: "Download asset bytes to disk. The CLI streams the raw bytes to --output; in --json mode --output is required and the JSON envelope only carries metadata.",
6265
+ command: "zinkee --json document-templates assets download 7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e 9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f --output ./contract.docx"
6266
+ },
6267
+ {
6268
+ description: "Stream asset bytes to stdout (omit --json and --output). Useful for piping into other tools.",
6269
+ command: "zinkee document-templates assets download 7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e 9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f > contract.docx"
6270
+ }
6271
+ ])
6272
+ };
6273
+ function fallbackExamples(commandName) {
6274
+ switch (commandName) {
6275
+ default:
6276
+ return [];
6277
+ }
6278
+ }
6279
+ function buildSchemaFieldCreateExamples(type) {
6280
+ const definitions = getSchemaFieldTypeDefinitions();
6281
+ const selectedDefinition = resolveSchemaFieldType(type);
6282
+ const filteredDefinitions = selectedDefinition ? definitions.filter((definition) => definition.key === selectedDefinition.key) : definitions;
6283
+ const flat = filteredDefinitions.flatMap(
6284
+ (definition) => definition.examplePayloads.map((example) => ({ definition, example }))
6285
+ );
6286
+ return buildCommandExamples(
6287
+ flat.map(({ example }, index) => {
6288
+ const slug = typeof example.payload.slug === "string" ? example.payload.slug : "field";
6289
+ const command = `zinkee --json schemas fields create contacts --raw '${JSON.stringify(example.payload)}'`;
6290
+ if (index === 0) {
6291
+ return {
6292
+ description: example.name,
6293
+ command,
6294
+ response: `{
6295
+ "schemaId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
6296
+ "fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890",
6297
+ "slug": "${slug}"
6298
+ }`
6299
+ };
6300
+ }
6301
+ return {
6302
+ description: example.name,
6303
+ command
6304
+ };
6305
+ })
6306
+ );
6307
+ }
6308
+ function getCommandExamples(commandName, filters = {}) {
6309
+ const examples = commandName === "schemas fields create" ? buildSchemaFieldCreateExamples(filters.type) : specificExamples[commandName] ?? fallbackExamples(commandName);
6310
+ return examples.map((example) => withResponseEnvelope(example, commandName));
6311
+ }
6312
+ function withResponseEnvelope(example, commandName) {
6313
+ if (example.response === void 0) {
6314
+ return example;
6315
+ }
6316
+ let payload;
6317
+ try {
6318
+ payload = JSON.parse(example.response);
6319
+ } catch {
6320
+ return example;
6321
+ }
6322
+ return {
6323
+ ...example,
6324
+ response: JSON.stringify(buildJsonSuccess(payload, { command: commandName }), null, 2)
6325
+ };
6326
+ }
6327
+ function getResolvedCommandExamples(commandName, filters = {}) {
6328
+ return getCommandExamples(commandName, filters);
6329
+ }
6330
+
6331
+ // src/cli-examples.ts
6332
+ function getStdout(stdout) {
6333
+ return stdout ?? process.stdout;
6334
+ }
6335
+ function extractExampleFilters(tokens) {
6336
+ const filters = {};
6337
+ for (let index = 0; index < tokens.length; index += 1) {
6338
+ const token = tokens[index];
6339
+ if (token === "--type") {
6340
+ const value = tokens[index + 1];
6341
+ if (value && !value.startsWith("-")) {
6342
+ filters.type = value;
6343
+ index += 1;
6344
+ }
6345
+ }
6346
+ }
6347
+ return filters;
5628
6348
  }
5629
6349
  function extractExampleRequest(argv, optionMeta = createDefaultOptionMetadata()) {
5630
6350
  if (!argv.includes("--example")) {
@@ -5634,7 +6354,22 @@ function extractExampleRequest(argv, optionMeta = createDefaultOptionMetadata())
5634
6354
  const operands = collectOperandTokens(userArgs, optionMeta, { skipFlag: "--example" });
5635
6355
  const cmdPath = findCommandPath(operands, commandRegistry);
5636
6356
  if (!cmdPath) {
5637
- throw new CliError("invalid_cli_usage", 'Provide a command after "--example".');
6357
+ const typed = operands.map((operand) => operand.token).join(" ");
6358
+ if (typed.length === 0) {
6359
+ throw new CliError("invalid_cli_usage", 'Provide a command after "--example".');
6360
+ }
6361
+ const subcommands = Object.keys(commandRegistry).filter(
6362
+ (name) => name.startsWith(`${typed} `)
6363
+ );
6364
+ if (subcommands.length > 0) {
6365
+ const list = subcommands.map((name) => ` zinkee --example ${name}`).join("\n");
6366
+ throw new CliError(
6367
+ "invalid_cli_usage",
6368
+ `"${typed}" is a command group, not a runnable command. Available subcommands:
6369
+ ${list}`
6370
+ );
6371
+ }
6372
+ throw new CliError("invalid_cli_usage", `Unknown command "${typed}".`);
5638
6373
  }
5639
6374
  const remainingTokens = userArgs.slice(cmdPath.lastArgvIndex + 1);
5640
6375
  return {
@@ -5680,7 +6415,7 @@ ${index + 1}. ${example.description}
5680
6415
 
5681
6416
  // src/program.ts
5682
6417
  import { createRequire } from "module";
5683
- import { Command as Command10 } from "commander";
6418
+ import { Command as Command11 } from "commander";
5684
6419
 
5685
6420
  // src/config.ts
5686
6421
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -6034,7 +6769,7 @@ var AutomationsApi = class {
6034
6769
  }
6035
6770
  moveAutomation(input) {
6036
6771
  return this.client.requestJson(
6037
- `/api/v2/automation-folders/${segment(input.targetFolderId)}/automations:move`,
6772
+ "/api/v2/automation-folders/automations:move",
6038
6773
  jsonOptions(input, "POST")
6039
6774
  );
6040
6775
  }
@@ -6441,11 +7176,11 @@ function hasSetPath(target, path) {
6441
7176
  return Object.hasOwn(target, path);
6442
7177
  }
6443
7178
  let current = target;
6444
- for (const segment3 of segments) {
6445
- if (!isJsonObject2(current) || !Object.hasOwn(current, segment3)) {
7179
+ for (const segment4 of segments) {
7180
+ if (!isJsonObject2(current) || !Object.hasOwn(current, segment4)) {
6446
7181
  return false;
6447
7182
  }
6448
- current = current[segment3];
7183
+ current = current[segment4];
6449
7184
  }
6450
7185
  return true;
6451
7186
  }
@@ -6908,6 +7643,15 @@ function buildActionInput(options, mode) {
6908
7643
  if (options.targetSchema !== void 0) {
6909
7644
  config.targetSchemaId = options.targetSchema;
6910
7645
  }
7646
+ if (options.to !== void 0) {
7647
+ mappings.recipients = options.to;
7648
+ }
7649
+ if (options.subject !== void 0) {
7650
+ mappings.subject = options.subject;
7651
+ }
7652
+ if (options.message !== void 0) {
7653
+ mappings.body = options.message;
7654
+ }
6911
7655
  if (!isEmptyObject(mappings)) {
6912
7656
  config.fieldMapping = mappings;
6913
7657
  }
@@ -6951,6 +7695,12 @@ function buildActionInput(options, mode) {
6951
7695
  if (!isEmptyObject(args)) {
6952
7696
  config.pluginArgs = args;
6953
7697
  }
7698
+ if (options.documentTemplate !== void 0) {
7699
+ config.documentTemplateId = options.documentTemplate;
7700
+ }
7701
+ if (options.targetFileField !== void 0) {
7702
+ config.targetFileFieldId = options.targetFileField;
7703
+ }
6954
7704
  if (!isEmptyObject(config)) {
6955
7705
  if (isJsonObject2(config.fieldMapping)) {
6956
7706
  config.fieldMapping = normalizeAutomationFieldMapping(
@@ -7137,19 +7887,35 @@ function addTriggerPayloadOptions(command) {
7137
7887
  []
7138
7888
  ).option("--webhook-endpoint-id <uuid>", "webhook endpoint id").option("--raw <json>", "raw trigger JSON payload").option("--raw-file <path>", "path to raw trigger JSON payload");
7139
7889
  }
7890
+ var ACTION_TYPES_HELP = `
7891
+ Action types and their flags:
7892
+ create_record --target-schema, --map, --map-json
7893
+ update_record --target-schema, --use-trigger-record, --map,
7894
+ --condition, --condition-json
7895
+ search_records --target-schema, --condition, --condition-json
7896
+ send_message --to, --subject, --message
7897
+ (--map / --map-json also accepted for extra keys)
7898
+ http_request --method, --url, --header, --body, --content-type,
7899
+ --timeout-ms, --var, --auth-mode, --connection
7900
+ execute_plugin --plugin, --arg, --arg-json
7901
+ generate_document --document-template, --target-schema, --target-file-field
7902
+ `;
7140
7903
  function addActionPayloadOptions(command) {
7141
- return command.option("--type <type>", "action type").option("--name <name>", "action name").option("--target-schema <uuid>", "target schema id").option("--map <field=value>", "field mapping", collectValues, []).option("--map-json <field=json>", "JSON field mapping", collectValues, []).option("--condition <field:comparator:value>", "action condition", collectValues, []).option("--condition-json <json>", "action condition as JSON", collectValues, []).option("--use-trigger-record", "use the triggering record").option("--method <method>", "HTTP method").option("--url <url>", "HTTP target URL").option("--header <key=value>", "HTTP header", collectValues, []).option("--body <json>", "HTTP request body as JSON").option("--body-file <path>", "path to HTTP request body JSON").option("--content-type <type>", "HTTP content type").option(
7904
+ return command.option(
7905
+ "--type <type>",
7906
+ "action type (create_record|update_record|search_records|send_message|http_request|execute_plugin|generate_document)"
7907
+ ).option("--name <name>", "action name").option("--target-schema <uuid>", "target schema id").option("--map <field=value>", "field mapping", collectValues, []).option("--map-json <field=json>", "JSON field mapping", collectValues, []).option("--condition <field:comparator:value>", "action condition", collectValues, []).option("--condition-json <json>", "action condition as JSON", collectValues, []).option("--use-trigger-record", "use the triggering record").option("--method <method>", "HTTP method").option("--url <url>", "HTTP target URL").option("--header <key=value>", "HTTP header", collectValues, []).option("--body <json>", "HTTP request body as JSON").option("--body-file <path>", "path to HTTP request body JSON").option("--content-type <type>", "HTTP content type").option(
7142
7908
  "--timeout-ms <n>",
7143
7909
  "HTTP timeout in milliseconds",
7144
7910
  parseNonNegativeInteger
7145
- ).option("--var <key=value>", "HTTP variable", collectValues, []).option("--auth-mode <mode>", "HTTP auth mode").option("--connection <uuid>", "connection id for HTTP auth").option("--plugin <id>", "plugin id for execute_plugin").option("--arg <key=value>", "plugin argument", collectValues, []).option("--arg-json <key=json>", "plugin argument as JSON", collectValues, []).option("--raw <json>", "raw action JSON payload").option("--raw-file <path>", "path to raw action JSON payload");
7911
+ ).option("--var <key=value>", "HTTP variable", collectValues, []).option("--auth-mode <mode>", "HTTP auth mode").option("--connection <uuid>", "connection id for HTTP auth").option("--plugin <id>", "plugin id for execute_plugin").option("--arg <key=value>", "plugin argument", collectValues, []).option("--arg-json <key=json>", "plugin argument as JSON", collectValues, []).option("--to <recipient>", "send_message recipient (maps to fieldMapping.recipients)").option("--subject <text>", "send_message subject").option("--message <text>", "send_message body text (maps to fieldMapping.body)").option("--document-template <uuid>", "generate_document template id").option("--target-file-field <uuid>", "generate_document target FileField id").option("--raw <json>", "raw action JSON payload").option("--raw-file <path>", "path to raw action JSON payload").addHelpText("after", ACTION_TYPES_HELP);
7146
7912
  }
7147
7913
  function registerAutomationsCommands(program, options = {}) {
7148
7914
  const filePath = options.configPath ?? defaultConfigPath;
7149
7915
  const stdout = getCommandIo2(options.stdout);
7150
7916
  const automations = program.command("automations").description("Manage automations, triggers, actions, and connections");
7151
- const folders = automations.command("folders").description("Manage automation folders");
7152
- folders.command("list").description("List automation folders").action(async function action() {
7917
+ const folders = automations.command("folders").description("Manage automation folders (flat: a folder only has a name, no order or properties)");
7918
+ folders.command("list").description("List automation folders (non-root folders plus the root bucket, whose name is null)").action(async function action() {
7153
7919
  const { api, json } = createAutomationsApi(this, filePath);
7154
7920
  const data = await api.listFolders();
7155
7921
  if (json) {
@@ -7199,7 +7965,11 @@ function registerAutomationsCommands(program, options = {}) {
7199
7965
  }
7200
7966
  writeLine2(stdout, `Deleted automation folder "${folder.value}".`);
7201
7967
  });
7202
- folders.command("move").description("Move an automation into a folder").argument("<automation>", "automation UUID", parseUuidOrSlugSelector).requiredOption("--to <folder>", "target folder UUID or exact name", parseUuidOrSlugSelector).action(async function action(automation, commandOptions) {
7968
+ folders.command("move").description("Move an automation into a folder").argument("<automation>", "automation UUID", parseUuidOrSlugSelector).requiredOption(
7969
+ "--to <folder>",
7970
+ "target folder UUID or exact name (use the root folder to move it out of any folder)",
7971
+ parseUuidOrSlugSelector
7972
+ ).action(async function action(automation, commandOptions) {
7203
7973
  const { api, json, readOnly } = createAutomationsApi(this, filePath);
7204
7974
  ensureWritable("automations folders move", readOnly, "write");
7205
7975
  if (!commandOptions.to) {
@@ -7884,9 +8654,117 @@ var DisplaysApi = class {
7884
8654
  { method: "DELETE" }
7885
8655
  );
7886
8656
  }
8657
+ listFolders() {
8658
+ return this.client.getJson("/api/v2/display-folders");
8659
+ }
8660
+ createFolder(body) {
8661
+ return this.client.requestJson(
8662
+ "/api/v2/display-folders",
8663
+ jsonOptions3(body, "POST")
8664
+ );
8665
+ }
8666
+ updateFolder(folder, body) {
8667
+ return this.client.requestJson(
8668
+ `/api/v2/display-folders/${selectorValue3(folder)}`,
8669
+ jsonOptions3(body, "PATCH")
8670
+ );
8671
+ }
8672
+ deleteFolder(folder) {
8673
+ return this.client.requestJson(
8674
+ `/api/v2/display-folders/${selectorValue3(folder)}`,
8675
+ { method: "DELETE" }
8676
+ );
8677
+ }
8678
+ moveDisplay(input) {
8679
+ return this.client.requestJson(
8680
+ "/api/v2/display-folders/displays:move",
8681
+ jsonOptions3(input, "POST")
8682
+ );
8683
+ }
7887
8684
  };
7888
8685
 
8686
+ // src/parsers/expressions.ts
8687
+ var supportedComparators = /* @__PURE__ */ new Set([
8688
+ "eq",
8689
+ "neq",
8690
+ "gt",
8691
+ "gte",
8692
+ "lt",
8693
+ "lte",
8694
+ "contains",
8695
+ "in",
8696
+ "is_empty",
8697
+ "is_not_empty"
8698
+ ]);
8699
+ var noValueComparators = /* @__PURE__ */ new Set(["is_empty", "is_not_empty"]);
8700
+ function parseColonExpression(input) {
8701
+ const parts = input.split(":");
8702
+ if (parts.length < 2) {
8703
+ throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
8704
+ }
8705
+ const field = parts[0]?.trim();
8706
+ const comparator = parts[1]?.trim();
8707
+ if (!field) {
8708
+ throw new Error(`Expected a field before ":", got "${input}".`);
8709
+ }
8710
+ if (!comparator) {
8711
+ throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
8712
+ }
8713
+ return [field, comparator, parts.slice(2).join(":").trim()];
8714
+ }
8715
+ function parseSpaceExpression(input) {
8716
+ const parts = input.trim().split(/\s+/);
8717
+ if (parts.length < 2) {
8718
+ throw new Error(`Expected a field comparator expression, got "${input}".`);
8719
+ }
8720
+ const field = parts[0];
8721
+ const comparator = parts[1];
8722
+ if (!field) {
8723
+ throw new Error(`Expected a field comparator expression, got "${input}".`);
8724
+ }
8725
+ if (!comparator) {
8726
+ throw new Error(`Expected a comparator after "${field}", got "${input}".`);
8727
+ }
8728
+ return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
8729
+ }
8730
+ function normalizeValue(comparator, rawValue) {
8731
+ if (noValueComparators.has(comparator)) {
8732
+ if (rawValue.length > 0) {
8733
+ throw new Error(`Comparator "${comparator}" does not accept a value.`);
8734
+ }
8735
+ return null;
8736
+ }
8737
+ if (comparator === "in") {
8738
+ const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
8739
+ if (values.length === 0) {
8740
+ throw new Error(`Comparator "${comparator}" requires at least one value.`);
8741
+ }
8742
+ return values;
8743
+ }
8744
+ if (rawValue.length === 0) {
8745
+ throw new Error(`Comparator "${comparator}" requires a value.`);
8746
+ }
8747
+ return rawValue;
8748
+ }
8749
+ function parseExpression(input) {
8750
+ const trimmed = input.trim();
8751
+ const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ") ? parseColonExpression(trimmed) : parseSpaceExpression(trimmed);
8752
+ if (!supportedComparators.has(comparatorCandidate)) {
8753
+ throw new Error(
8754
+ `Unsupported comparator "${comparatorCandidate}" in expression "${input}".`
8755
+ );
8756
+ }
8757
+ const comparator = comparatorCandidate;
8758
+ return {
8759
+ field,
8760
+ comparator,
8761
+ value: normalizeValue(comparator, rawValue),
8762
+ raw: trimmed
8763
+ };
8764
+ }
8765
+
7889
8766
  // src/commands/displays.ts
8767
+ var DATE_DYNAMIC_OPTIONS = "today, thisWeek, thisMonth, thisQuarter, thisSemester, thisYear, next7Days, next30Days, next6Months, next12Months, last12Months";
7890
8768
  function getCommandIo4(stdout) {
7891
8769
  return stdout ?? process.stdout;
7892
8770
  }
@@ -7950,6 +8828,37 @@ function parseUuidOrSlugSelector3(input) {
7950
8828
  throw new InvalidArgumentError3(message);
7951
8829
  }
7952
8830
  }
8831
+ var COMPARATOR_TO_DISPLAY_OPERATOR = {
8832
+ eq: "EQUAL",
8833
+ neq: "NOT_EQUAL",
8834
+ gt: "GREATER",
8835
+ gte: "GREATER_OR_EQUAL",
8836
+ lt: "LESS",
8837
+ lte: "LESS_OR_EQUAL",
8838
+ contains: "CONTAINS",
8839
+ in: "ANY_OF",
8840
+ is_empty: "IS_EMPTY",
8841
+ is_not_empty: "IS_NOT_EMPTY"
8842
+ };
8843
+ function toDisplayFilter(parsed) {
8844
+ const values = parsed.value === null ? [] : Array.isArray(parsed.value) ? [...parsed.value] : [parsed.value];
8845
+ return {
8846
+ origin: { fieldId: parsed.field },
8847
+ comparisonOperator: COMPARATOR_TO_DISPLAY_OPERATOR[parsed.comparator],
8848
+ values
8849
+ };
8850
+ }
8851
+ function parseFilterExpression(input) {
8852
+ try {
8853
+ return parseExpression(input);
8854
+ } catch (error) {
8855
+ const message = error instanceof Error ? error.message : "Invalid filter expression.";
8856
+ throw new InvalidArgumentError3(message);
8857
+ }
8858
+ }
8859
+ function collectFilters(value, previous = []) {
8860
+ return [...previous, parseFilterExpression(value)];
8861
+ }
7953
8862
  function readRawObject2(raw, rawFile) {
7954
8863
  if (raw && rawFile) {
7955
8864
  throw new CliError("invalid_cli_usage", 'Use either "--raw" or "--raw-file", not both.');
@@ -7984,8 +8893,152 @@ function readNamedJsonOption(raw, rawFile, inlineFlag = "--option", fileFlag = "
7984
8893
  function isEmptyObject3(value) {
7985
8894
  return Object.keys(value).length === 0;
7986
8895
  }
7987
- function displayRows(displays) {
7988
- return displays.map((display) => ({
8896
+ function isPlainObject(value) {
8897
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8898
+ }
8899
+ function deepMerge(target, source) {
8900
+ const out = { ...target };
8901
+ for (const [key, value] of Object.entries(source)) {
8902
+ const existing = out[key];
8903
+ out[key] = isPlainObject(existing) && isPlainObject(value) ? deepMerge(existing, value) : value;
8904
+ }
8905
+ return out;
8906
+ }
8907
+ function assignDefined(target, key, value) {
8908
+ if (value !== void 0) target[key] = value;
8909
+ }
8910
+ function readNamedJsonObject(raw, rawFile, inlineFlag, fileFlag) {
8911
+ const parsed = readNamedJsonOption(raw, rawFile, inlineFlag, fileFlag);
8912
+ if (parsed === void 0) return void 0;
8913
+ if (!isPlainObject(parsed)) {
8914
+ throw new CliError("invalid_cli_usage", `${inlineFlag} payload must be a JSON object.`);
8915
+ }
8916
+ return parsed;
8917
+ }
8918
+ var HIERARCHY_AGGREGATIONS = /* @__PURE__ */ new Set([
8919
+ "SUM",
8920
+ "AVERAGE",
8921
+ "MINIMUM",
8922
+ "MAXIMUM",
8923
+ "COUNT",
8924
+ "COUNT_DISTINCT"
8925
+ ]);
8926
+ function parseHierarchyInfo(value) {
8927
+ const idx = value.indexOf(":");
8928
+ if (idx <= 0 || idx === value.length - 1) {
8929
+ throw new CliError("invalid_cli_usage", `--hierarchy-info "${value}" must use the format <id:name>.`);
8930
+ }
8931
+ return { id: value.slice(0, idx).trim(), name: value.slice(idx + 1).trim() };
8932
+ }
8933
+ function parseHierarchyNumber(value) {
8934
+ const parts = value.split(":");
8935
+ if (parts.length < 3) {
8936
+ throw new CliError(
8937
+ "invalid_cli_usage",
8938
+ `--hierarchy-number "${value}" must use the format <id:name:aggregation>.`
8939
+ );
8940
+ }
8941
+ const id = parts[0]?.trim() ?? "";
8942
+ const aggregation = (parts[parts.length - 1]?.trim() ?? "").toUpperCase();
8943
+ const name = parts.slice(1, -1).join(":").trim();
8944
+ if (!id || !name) {
8945
+ throw new CliError(
8946
+ "invalid_cli_usage",
8947
+ `--hierarchy-number "${value}" must use the format <id:name:aggregation>.`
8948
+ );
8949
+ }
8950
+ if (!HIERARCHY_AGGREGATIONS.has(aggregation)) {
8951
+ throw new CliError(
8952
+ "invalid_cli_usage",
8953
+ `--hierarchy-number aggregation "${aggregation}" must be one of SUM, AVERAGE, MINIMUM, MAXIMUM, COUNT, COUNT_DISTINCT.`
8954
+ );
8955
+ }
8956
+ return { id, name, aggregation };
8957
+ }
8958
+ function parseHierarchyMapping(value, flag) {
8959
+ const idx = value.indexOf(":");
8960
+ if (idx <= 0 || idx === value.length - 1) {
8961
+ throw new CliError("invalid_cli_usage", `${flag} "${value}" must use the format <columnId:fieldId>.`);
8962
+ }
8963
+ return { columnId: value.slice(0, idx).trim(), fieldId: value.slice(idx + 1).trim() };
8964
+ }
8965
+ function buildWidgetFieldMapping(options) {
8966
+ const mapping = {};
8967
+ assignDefined(mapping, "columnField", options.columnField);
8968
+ assignDefined(mapping, "swimlaneField", options.swimlaneField);
8969
+ assignDefined(mapping, "startDateField", options.startDateField);
8970
+ assignDefined(mapping, "endDateField", options.endDateField);
8971
+ assignDefined(mapping, "labelField", options.labelField);
8972
+ assignDefined(mapping, "groupField", options.groupField);
8973
+ assignDefined(mapping, "subgroupField", options.subgroupField);
8974
+ assignDefined(mapping, "progressField", options.progressField);
8975
+ const cardConfig = {};
8976
+ assignDefined(cardConfig, "titleField", options.cardTitleField);
8977
+ assignDefined(cardConfig, "category1Field", options.cardCategory1Field);
8978
+ assignDefined(cardConfig, "category2Field", options.cardCategory2Field);
8979
+ assignDefined(cardConfig, "category3Field", options.cardCategory3Field);
8980
+ assignDefined(cardConfig, "category4Field", options.cardCategory4Field);
8981
+ assignDefined(cardConfig, "descriptionField", options.cardDescriptionField);
8982
+ if (Object.keys(cardConfig).length > 0) mapping.cardConfig = cardConfig;
8983
+ const aggregationConfig = {};
8984
+ assignDefined(aggregationConfig, "method", options.aggregationMethod);
8985
+ assignDefined(aggregationConfig, "fieldId", options.aggregationField);
8986
+ if (Object.keys(aggregationConfig).length > 0) mapping.aggregationConfig = aggregationConfig;
8987
+ return Object.keys(mapping).length > 0 ? mapping : void 0;
8988
+ }
8989
+ function buildHierarchyConfig(options) {
8990
+ const base = readNamedJsonObject(
8991
+ options.hierarchyConfig,
8992
+ options.hierarchyConfigFile,
8993
+ "--hierarchy-config",
8994
+ "--hierarchy-config-file"
8995
+ );
8996
+ const patch = {};
8997
+ const columns = {};
8998
+ assignDefined(columns, "label", options.hierarchyLabel);
8999
+ if (options.hierarchyInfo && options.hierarchyInfo.length > 0) {
9000
+ columns.infos = options.hierarchyInfo.map(parseHierarchyInfo);
9001
+ }
9002
+ if (options.hierarchyNumber && options.hierarchyNumber.length > 0) {
9003
+ columns.numbers = options.hierarchyNumber.map(parseHierarchyNumber);
9004
+ }
9005
+ if (Object.keys(columns).length > 0) patch.columns = columns;
9006
+ const rootLevel = {};
9007
+ assignDefined(rootLevel, "name", options.hierarchyRootName);
9008
+ assignDefined(rootLevel, "labelFieldId", options.hierarchyRootLabelField);
9009
+ if (options.hierarchyRootInfo && options.hierarchyRootInfo.length > 0) {
9010
+ rootLevel.infoMappings = options.hierarchyRootInfo.map(
9011
+ (entry) => parseHierarchyMapping(entry, "--hierarchy-root-info")
9012
+ );
9013
+ }
9014
+ if (options.hierarchyRootNumber && options.hierarchyRootNumber.length > 0) {
9015
+ rootLevel.numberMappings = options.hierarchyRootNumber.map(
9016
+ (entry) => parseHierarchyMapping(entry, "--hierarchy-root-number")
9017
+ );
9018
+ }
9019
+ if (Object.keys(rootLevel).length > 0) patch.rootLevel = rootLevel;
9020
+ if (options.hierarchyChild && options.hierarchyChild.length > 0) {
9021
+ patch.childLevels = options.hierarchyChild.map((entry, index) => {
9022
+ let parsed;
9023
+ try {
9024
+ parsed = JSON.parse(entry);
9025
+ } catch {
9026
+ throw new CliError("invalid_cli_usage", `--hierarchy-child payload #${index + 1} is invalid JSON.`);
9027
+ }
9028
+ if (!isPlainObject(parsed)) {
9029
+ throw new CliError(
9030
+ "invalid_cli_usage",
9031
+ `--hierarchy-child payload #${index + 1} must be a JSON object.`
9032
+ );
9033
+ }
9034
+ return parsed;
9035
+ });
9036
+ }
9037
+ if (base === void 0 && Object.keys(patch).length === 0) return void 0;
9038
+ return deepMerge(base ?? {}, patch);
9039
+ }
9040
+ function displayRows(displays) {
9041
+ return displays.map((display) => ({
7989
9042
  id: display.id,
7990
9043
  name: display.name ?? "",
7991
9044
  layoutTemplate: display.layoutTemplate ?? "",
@@ -7999,6 +9052,31 @@ function getDisplaySubresource(value) {
7999
9052
  }
8000
9053
  return value.filter((entry) => Boolean(entry) && typeof entry === "object");
8001
9054
  }
9055
+ function displayFolderRows(folders) {
9056
+ return folders.map((folder) => ({
9057
+ id: folder.id ?? "",
9058
+ name: folder.name ?? "",
9059
+ isRoot: folder.isRoot ?? "",
9060
+ displayCount: folder.displayCount ?? ""
9061
+ }));
9062
+ }
9063
+ async function resolveDisplayFolderId(api, selector) {
9064
+ if (selector.kind === "uuid") {
9065
+ return selector.value;
9066
+ }
9067
+ const folders = await api.listFolders();
9068
+ const matches = folders.filter((folder) => folder.name === selector.value);
9069
+ if (matches.length === 1 && typeof matches[0]?.id === "string" && matches[0].id.length > 0) {
9070
+ return matches[0].id;
9071
+ }
9072
+ if (matches.length > 1) {
9073
+ throw new CliError(
9074
+ "invalid_cli_usage",
9075
+ `Folder "${selector.value}" is ambiguous. Found ${matches.length} matches.`
9076
+ );
9077
+ }
9078
+ throw new CliError("invalid_cli_usage", `Folder "${selector.value}" was not found.`);
9079
+ }
8002
9080
  function buildVariableBody(options) {
8003
9081
  const body = {
8004
9082
  ...readRawObject2(options.raw, options.rawFile)
@@ -8016,7 +9094,7 @@ function buildVariableBody(options) {
8016
9094
  };
8017
9095
  }
8018
9096
  if (options.filter && options.filter.length > 0) {
8019
- body.filters = options.filter.map((entry) => ({ raw: entry }));
9097
+ body.filters = options.filter.map(toDisplayFilter);
8020
9098
  }
8021
9099
  if (options.dateMode || options.dateDynamicOption || options.dateSpecific || options.dateRangeStart || options.dateRangeEnd) {
8022
9100
  body.date = {
@@ -8065,15 +9143,39 @@ function buildWidgetBody(options) {
8065
9143
  layout
8066
9144
  };
8067
9145
  }
8068
- if (options.field?.length || options.sort?.length || options.filter?.length) {
8069
- body.view = {
8070
- ...typeof body.view === "object" && body.view ? body.view : {},
8071
- fields: options.field
8072
- };
9146
+ const viewPatch = {};
9147
+ if (options.field && options.field.length > 0) {
9148
+ viewPatch.fields = options.field;
9149
+ }
9150
+ const fieldMapping = buildWidgetFieldMapping(options);
9151
+ const viewStateBase = readNamedJsonObject(
9152
+ options.viewState,
9153
+ options.viewStateFile,
9154
+ "--view-state",
9155
+ "--view-state-file"
9156
+ );
9157
+ let viewState = viewStateBase ? { ...viewStateBase } : void 0;
9158
+ if (fieldMapping) {
9159
+ viewState = deepMerge(viewState ?? {}, { fieldMapping });
9160
+ }
9161
+ if (viewState !== void 0) {
9162
+ viewPatch.viewAdditionalProperties = { viewState };
9163
+ }
9164
+ const hierarchyConfig = buildHierarchyConfig(options);
9165
+ if (hierarchyConfig !== void 0) {
9166
+ viewPatch.hierarchyConfig = hierarchyConfig;
9167
+ }
9168
+ if (Object.keys(viewPatch).length > 0) {
9169
+ const existingView = typeof body.view === "object" && body.view ? body.view : {};
9170
+ body.view = deepMerge(existingView, viewPatch);
9171
+ }
9172
+ const queryMerge = {};
9173
+ if (options.sort && options.sort.length > 0) queryMerge.sorts = options.sort;
9174
+ if (options.filter && options.filter.length > 0) queryMerge.filters = options.filter.map(toDisplayFilter);
9175
+ if (Object.keys(queryMerge).length > 0) {
8073
9176
  body.query = {
8074
9177
  ...typeof body.query === "object" && body.query ? body.query : {},
8075
- sorts: options.sort,
8076
- filters: options.filter?.map((entry) => ({ raw: entry }))
9178
+ ...queryMerge
8077
9179
  };
8078
9180
  }
8079
9181
  if (options.widgetReadOnly !== void 0 || options.hideAddButton !== void 0 || options.hideDeleteButton !== void 0 || options.hideSortButton !== void 0 || options.hideFilterButton !== void 0 || options.hideSeeButton !== void 0) {
@@ -8200,6 +9302,72 @@ function registerDisplaysCommands(program, options = {}) {
8200
9302
  }
8201
9303
  writeLine4(stdout, `Deleted display "${display.value}".`);
8202
9304
  });
9305
+ const folders = displays.command("folders").description("Manage display folders (flat: a folder only has a name, no order or properties)");
9306
+ folders.command("list").description("List display folders (non-root folders plus the root bucket, whose name is null)").action(async function action() {
9307
+ const { api, json } = createDisplaysApi(this, filePath);
9308
+ const data = await api.listFolders();
9309
+ if (json) {
9310
+ emitJson4(stdout, "displays folders list", data);
9311
+ return;
9312
+ }
9313
+ printTable(displayFolderRows(data), ["id", "name", "isRoot", "displayCount"], stdout);
9314
+ });
9315
+ folders.command("create").description("Create a display folder").requiredOption("--name <name>", "folder name").action(async function action(commandOptions) {
9316
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9317
+ ensureWritable3("displays folders create", readOnly, "write");
9318
+ if (commandOptions.name === void 0) {
9319
+ throw new CliError("invalid_cli_usage", 'Provide folder name via "--name".');
9320
+ }
9321
+ const data = await api.createFolder({ name: commandOptions.name });
9322
+ if (json) {
9323
+ emitJson4(stdout, "displays folders create", data);
9324
+ return;
9325
+ }
9326
+ writeLine4(stdout, `Created display folder "${String(data.name ?? commandOptions.name)}".`);
9327
+ });
9328
+ folders.command("update").description("Rename a display folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector3).requiredOption("--name <name>", "folder name").action(async function action(folder, commandOptions) {
9329
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9330
+ ensureWritable3("displays folders update", readOnly, "write");
9331
+ if (commandOptions.name === void 0) {
9332
+ throw new CliError("invalid_cli_usage", 'Provide folder name via "--name".');
9333
+ }
9334
+ const folderId = await resolveDisplayFolderId(api, folder);
9335
+ const data = await api.updateFolder({ kind: "uuid", value: folderId }, { name: commandOptions.name });
9336
+ if (json) {
9337
+ emitJson4(stdout, "displays folders update", data);
9338
+ return;
9339
+ }
9340
+ writeLine4(stdout, `Updated display folder "${String(data.name ?? folder.value)}".`);
9341
+ });
9342
+ folders.command("delete").description("Delete a display folder (its displays move back to the root folder)").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector3).action(async function action(folder) {
9343
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9344
+ ensureWritable3("displays folders delete", readOnly, "destructive");
9345
+ const folderId = await resolveDisplayFolderId(api, folder);
9346
+ const data = await api.deleteFolder({ kind: "uuid", value: folderId });
9347
+ if (json) {
9348
+ emitJson4(stdout, "displays folders delete", data);
9349
+ return;
9350
+ }
9351
+ writeLine4(stdout, `Deleted display folder "${folder.value}".`);
9352
+ });
9353
+ folders.command("move").description("Move a display into a folder").argument("<display>", "display UUID", parseUuidOrSlugSelector3).requiredOption(
9354
+ "--to <folder>",
9355
+ "target folder UUID or exact name (use the root folder to move it out of any folder)",
9356
+ parseUuidOrSlugSelector3
9357
+ ).action(async function action(display, commandOptions) {
9358
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9359
+ ensureWritable3("displays folders move", readOnly, "write");
9360
+ if (!commandOptions.to) {
9361
+ throw new CliError("invalid_cli_usage", 'Provide target folder via "--to".');
9362
+ }
9363
+ const targetFolderId = await resolveDisplayFolderId(api, commandOptions.to);
9364
+ const data = await api.moveDisplay({ displayId: display.value, targetFolderId });
9365
+ if (json) {
9366
+ emitJson4(stdout, "displays folders move", data);
9367
+ return;
9368
+ }
9369
+ writeLine4(stdout, `Moved display "${display.value}" to folder "${targetFolderId}".`);
9370
+ });
8203
9371
  const layout = displays.command("layout").description("Manage display layout");
8204
9372
  layout.command("get").description("Get display layout").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
8205
9373
  const { api, json } = createDisplaysApi(this, filePath);
@@ -8288,7 +9456,7 @@ function registerDisplaysCommands(program, options = {}) {
8288
9456
  }
8289
9457
  printTable([match], void 0, stdout);
8290
9458
  });
8291
- variables.command("create").description("Create variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", (v, p = []) => [...p, v], []).option("--date-mode <mode>").option("--date-dynamic-option <option>").option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
9459
+ variables.command("create").description("Create variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", collectFilters, []).option("--date-mode <mode>").option("--date-dynamic-option <option>", `dynamic date period (${DATE_DYNAMIC_OPTIONS})`).option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
8292
9460
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8293
9461
  ensureWritable3("displays variables create", readOnly, "write");
8294
9462
  const data = await api.createVariable(display, buildVariableBody(commandOptions));
@@ -8298,7 +9466,7 @@ function registerDisplaysCommands(program, options = {}) {
8298
9466
  }
8299
9467
  writeLine4(stdout, `Created variable on display "${display.value}".`);
8300
9468
  });
8301
- variables.command("update").description("Update variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<variable>", "variable id").option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", (v, p = []) => [...p, v], []).option("--date-mode <mode>").option("--date-dynamic-option <option>").option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, variableId, commandOptions) {
9469
+ variables.command("update").description("Update variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<variable>", "variable id").option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", collectFilters, []).option("--date-mode <mode>").option("--date-dynamic-option <option>", `dynamic date period (${DATE_DYNAMIC_OPTIONS})`).option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, variableId, commandOptions) {
8302
9470
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8303
9471
  ensureWritable3("displays variables update", readOnly, "write");
8304
9472
  const data = await api.updateVariable(display, variableId, buildVariableBody(commandOptions));
@@ -8338,117 +9506,520 @@ function registerDisplaysCommands(program, options = {}) {
8338
9506
  }
8339
9507
  printTable([data], void 0, stdout);
8340
9508
  });
8341
- widgets.command("create").description("Create widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--type <type>", "widget type").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", (v, p = []) => [...p, v], []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
9509
+ widgets.command("create").description("Create widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--type <type>", "widget type").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>", "chart type (lines, bars, stackedBars, combined)").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>", "time granularity (DATE, MONTH_YEAR, QUARTER_YEAR, YEAR)").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--column-field <uuid>", "KANBAN column field (UUID, not slug)").option("--swimlane-field <uuid>", "KANBAN swimlane field (UUID, not slug)").option("--start-date-field <uuid>", "TIMELINE start date field (UUID, not slug)").option("--end-date-field <uuid>", "TIMELINE end date field (UUID, not slug)").option("--label-field <uuid>", "TIMELINE label field (UUID, not slug)").option("--group-field <uuid>", "TIMELINE group field (UUID, not slug)").option("--subgroup-field <uuid>", "TIMELINE subgroup field (UUID, not slug)").option("--progress-field <uuid>", "TIMELINE progress field (UUID, not slug)").option("--card-title-field <uuid>", "KANBAN/TIMELINE card title field (UUID, not slug)").option("--card-category1-field <uuid>", "KANBAN/TIMELINE card category 1 field (UUID, not slug)").option("--card-category2-field <uuid>", "KANBAN/TIMELINE card category 2 field (UUID, not slug)").option("--card-category3-field <uuid>", "KANBAN card category 3 field (UUID, not slug)").option("--card-category4-field <uuid>", "TIMELINE card category 4 field (UUID, not slug)").option("--card-description-field <uuid>", "KANBAN/TIMELINE card description field (UUID, not slug)").option("--aggregation-method <method>", "KANBAN card aggregation method (e.g. sum)").option("--aggregation-field <uuid>", "KANBAN card aggregation field (UUID, not slug)").option("--view-state <json>", "KANBAN/TIMELINE viewState UI scalars (columnOrder, granularity, sidebarWidth, show* ...)").option("--view-state-file <path>", "read --view-state JSON from a file").option("--hierarchy-label <name>", "HIERARCHY label column name").option("--hierarchy-info <id:name>", "HIERARCHY info column; id is a unique opaque column id you choose (e.g. col_1779975207355_zfndknslw), not the name, and is reused as columnId in mappings (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-number <id:name:aggregation>", "HIERARCHY number column; id is a unique opaque column id (e.g. col_1779975207355_zfndknslw), not the name; aggregation SUM|AVERAGE|MINIMUM|MAXIMUM|COUNT|COUNT_DISTINCT (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-name <name>", "HIERARCHY root level name").option("--hierarchy-root-label-field <uuid>", "HIERARCHY root level label field (UUID, not slug)").option("--hierarchy-root-info <columnId:fieldId>", "HIERARCHY root info mapping; columnId must match a --hierarchy-info id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-number <columnId:fieldId>", "HIERARCHY root number mapping; columnId must match a --hierarchy-number id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-child <json>", "HIERARCHY child level JSON {name,schemaId,childFieldId,labelFieldId,infoMappings,numberMappings} (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-config <json>", "HIERARCHY full hierarchyConfig JSON (escape hatch)").option("--hierarchy-config-file <path>", "read --hierarchy-config JSON from a file").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
8342
9510
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8343
9511
  ensureWritable3("displays widgets create", readOnly, "write");
8344
9512
  const data = await api.createWidget(display, buildWidgetBody(commandOptions));
8345
9513
  if (json) {
8346
- emitJson4(stdout, "displays widgets create", data);
9514
+ emitJson4(stdout, "displays widgets create", data);
9515
+ return;
9516
+ }
9517
+ writeLine4(stdout, `Created widget on display "${display.value}".`);
9518
+ });
9519
+ widgets.command("update").description("Update widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").option("--type <type>").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>", "chart type (lines, bars, stackedBars, combined)").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>", "time granularity (DATE, MONTH_YEAR, QUARTER_YEAR, YEAR)").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--column-field <uuid>", "KANBAN column field (UUID, not slug)").option("--swimlane-field <uuid>", "KANBAN swimlane field (UUID, not slug)").option("--start-date-field <uuid>", "TIMELINE start date field (UUID, not slug)").option("--end-date-field <uuid>", "TIMELINE end date field (UUID, not slug)").option("--label-field <uuid>", "TIMELINE label field (UUID, not slug)").option("--group-field <uuid>", "TIMELINE group field (UUID, not slug)").option("--subgroup-field <uuid>", "TIMELINE subgroup field (UUID, not slug)").option("--progress-field <uuid>", "TIMELINE progress field (UUID, not slug)").option("--card-title-field <uuid>", "KANBAN/TIMELINE card title field (UUID, not slug)").option("--card-category1-field <uuid>", "KANBAN/TIMELINE card category 1 field (UUID, not slug)").option("--card-category2-field <uuid>", "KANBAN/TIMELINE card category 2 field (UUID, not slug)").option("--card-category3-field <uuid>", "KANBAN card category 3 field (UUID, not slug)").option("--card-category4-field <uuid>", "TIMELINE card category 4 field (UUID, not slug)").option("--card-description-field <uuid>", "KANBAN/TIMELINE card description field (UUID, not slug)").option("--aggregation-method <method>", "KANBAN card aggregation method (e.g. sum)").option("--aggregation-field <uuid>", "KANBAN card aggregation field (UUID, not slug)").option("--view-state <json>", "KANBAN/TIMELINE viewState UI scalars (columnOrder, granularity, sidebarWidth, show* ...)").option("--view-state-file <path>", "read --view-state JSON from a file").option("--hierarchy-label <name>", "HIERARCHY label column name").option("--hierarchy-info <id:name>", "HIERARCHY info column; id is a unique opaque column id you choose (e.g. col_1779975207355_zfndknslw), not the name, and is reused as columnId in mappings (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-number <id:name:aggregation>", "HIERARCHY number column; id is a unique opaque column id (e.g. col_1779975207355_zfndknslw), not the name; aggregation SUM|AVERAGE|MINIMUM|MAXIMUM|COUNT|COUNT_DISTINCT (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-name <name>", "HIERARCHY root level name").option("--hierarchy-root-label-field <uuid>", "HIERARCHY root level label field (UUID, not slug)").option("--hierarchy-root-info <columnId:fieldId>", "HIERARCHY root info mapping; columnId must match a --hierarchy-info id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-number <columnId:fieldId>", "HIERARCHY root number mapping; columnId must match a --hierarchy-number id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-child <json>", "HIERARCHY child level JSON {name,schemaId,childFieldId,labelFieldId,infoMappings,numberMappings} (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-config <json>", "HIERARCHY full hierarchyConfig JSON (escape hatch)").option("--hierarchy-config-file <path>", "read --hierarchy-config JSON from a file").option("--raw <json>").option("--raw-file <path>").action(async function action(display, widgetId, commandOptions) {
9520
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9521
+ ensureWritable3("displays widgets update", readOnly, "write");
9522
+ const data = await api.updateWidget(display, widgetId, buildWidgetBody(commandOptions));
9523
+ if (json) {
9524
+ emitJson4(stdout, "displays widgets update", data);
9525
+ return;
9526
+ }
9527
+ writeLine4(stdout, `Updated widget "${widgetId}".`);
9528
+ });
9529
+ widgets.command("delete").description("Delete widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").action(async function action(display, widgetId) {
9530
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9531
+ ensureWritable3("displays widgets delete", readOnly, "destructive");
9532
+ const data = await api.deleteWidget(display, widgetId);
9533
+ if (json) {
9534
+ emitJson4(stdout, "displays widgets delete", data);
9535
+ return;
9536
+ }
9537
+ writeLine4(stdout, `Deleted widget "${widgetId}".`);
9538
+ });
9539
+ const targets = displays.command("navigation-targets").description("Manage navigation targets");
9540
+ targets.command("list").description("List navigation targets").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
9541
+ const { api, json } = createDisplaysApi(this, filePath);
9542
+ const data = await api.getDisplay(display);
9543
+ const items = getDisplaySubresource(data.navigationTargets);
9544
+ if (json) {
9545
+ emitJson4(stdout, "displays navigation-targets list", items);
9546
+ return;
9547
+ }
9548
+ printTable(items, void 0, stdout);
9549
+ });
9550
+ targets.command("create").description("Create navigation target").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--source-widget <id>").requiredOption("--target-display <id>").requiredOption("--target-variable <id>").option("--label <label>").action(async function action(display, commandOptions) {
9551
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9552
+ ensureWritable3("displays navigation-targets create", readOnly, "write");
9553
+ const data = await api.createNavigationTarget(display, buildNavigationTargetBody(commandOptions));
9554
+ if (json) {
9555
+ emitJson4(stdout, "displays navigation-targets create", data);
9556
+ return;
9557
+ }
9558
+ writeLine4(stdout, `Created navigation target on display "${display.value}".`);
9559
+ });
9560
+ targets.command("update").description("Update navigation target").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<target>", "target id").option("--source-widget <id>").option("--target-display <id>").option("--target-variable <id>").option("--label <label>").action(async function action(display, targetId, commandOptions) {
9561
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9562
+ ensureWritable3("displays navigation-targets update", readOnly, "write");
9563
+ const data = await api.updateNavigationTarget(display, targetId, buildNavigationTargetBody(commandOptions));
9564
+ if (json) {
9565
+ emitJson4(stdout, "displays navigation-targets update", data);
9566
+ return;
9567
+ }
9568
+ writeLine4(stdout, `Updated navigation target "${targetId}".`);
9569
+ });
9570
+ targets.command("delete").description("Delete navigation target").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<target>", "target id").action(async function action(display, targetId) {
9571
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9572
+ ensureWritable3("displays navigation-targets delete", readOnly, "destructive");
9573
+ const data = await api.deleteNavigationTarget(display, targetId);
9574
+ if (json) {
9575
+ emitJson4(stdout, "displays navigation-targets delete", data);
9576
+ return;
9577
+ }
9578
+ writeLine4(stdout, `Deleted navigation target "${targetId}".`);
9579
+ });
9580
+ const cross = displays.command("cross-widget-filter").description("Manage cross widget filter");
9581
+ cross.command("get").description("Get cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
9582
+ const { api, json } = createDisplaysApi(this, filePath);
9583
+ const data = await api.getDisplay(display);
9584
+ const item = data.crossWidgetFilter ?? null;
9585
+ if (json) {
9586
+ emitJson4(stdout, "displays cross-widget-filter get", item);
9587
+ return;
9588
+ }
9589
+ printTable(item ? [item] : [], void 0, stdout);
9590
+ });
9591
+ cross.command("set").description("Set cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--source-widget <id>").requiredOption("--source-field <id>").requiredOption("--target <widget:field>", "repeatable target", (v, p = []) => [...p, v], []).action(async function action(display, commandOptions) {
9592
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9593
+ ensureWritable3("displays cross-widget-filter set", readOnly, "write");
9594
+ const data = await api.setCrossWidgetFilter(display, buildCrossWidgetFilterBody(commandOptions));
9595
+ if (json) {
9596
+ emitJson4(stdout, "displays cross-widget-filter set", data);
9597
+ return;
9598
+ }
9599
+ writeLine4(stdout, `Updated cross widget filter for display "${display.value}".`);
9600
+ });
9601
+ cross.command("delete").description("Delete cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
9602
+ const { api, json, readOnly } = createDisplaysApi(this, filePath);
9603
+ ensureWritable3("displays cross-widget-filter delete", readOnly, "destructive");
9604
+ const data = await api.deleteCrossWidgetFilter(display);
9605
+ if (json) {
9606
+ emitJson4(stdout, "displays cross-widget-filter delete", data);
9607
+ return;
9608
+ }
9609
+ writeLine4(stdout, `Deleted cross widget filter for display "${display.value}".`);
9610
+ });
9611
+ }
9612
+
9613
+ // src/commands/document-templates.ts
9614
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
9615
+ import { basename } from "path";
9616
+ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
9617
+
9618
+ // src/api/document-templates.ts
9619
+ function jsonOptions4(body, method) {
9620
+ return {
9621
+ method,
9622
+ headers: {
9623
+ "content-type": "application/json"
9624
+ },
9625
+ body: JSON.stringify(body)
9626
+ };
9627
+ }
9628
+ function selectorValue4(selector) {
9629
+ return encodeURIComponent(selector.value);
9630
+ }
9631
+ function segment3(value) {
9632
+ return encodeURIComponent(value);
9633
+ }
9634
+ var DocumentTemplatesApi = class {
9635
+ constructor(client) {
9636
+ this.client = client;
9637
+ }
9638
+ listTemplates() {
9639
+ return this.client.getJson("/api/v2/document-templates");
9640
+ }
9641
+ createTemplate(input) {
9642
+ return this.client.requestJson(
9643
+ "/api/v2/document-templates",
9644
+ jsonOptions4(input, "POST")
9645
+ );
9646
+ }
9647
+ updateTemplate(template, input) {
9648
+ return this.client.requestJson(
9649
+ `/api/v2/document-templates/${selectorValue4(template)}`,
9650
+ jsonOptions4(input, "PATCH")
9651
+ );
9652
+ }
9653
+ deleteTemplate(template) {
9654
+ return this.client.requestJson(
9655
+ `/api/v2/document-templates/${selectorValue4(template)}`,
9656
+ { method: "DELETE" }
9657
+ );
9658
+ }
9659
+ addAsset(template, input) {
9660
+ return this.client.requestJson(
9661
+ `/api/v2/document-templates/${selectorValue4(template)}/assets`,
9662
+ { method: "POST", body: input }
9663
+ );
9664
+ }
9665
+ deleteAsset(template, assetId) {
9666
+ return this.client.requestJson(
9667
+ `/api/v2/document-templates/${selectorValue4(template)}/assets/${segment3(assetId)}`,
9668
+ { method: "DELETE" }
9669
+ );
9670
+ }
9671
+ downloadAsset(template, assetId) {
9672
+ return this.client.requestBinary(
9673
+ `/api/v2/document-templates/${selectorValue4(template)}/assets/${segment3(assetId)}/download`
9674
+ );
9675
+ }
9676
+ };
9677
+
9678
+ // src/commands/document-templates.ts
9679
+ function getCommandIo5(stdout) {
9680
+ return stdout ?? process.stdout;
9681
+ }
9682
+ function writeLine5(stdout, value) {
9683
+ stdout.write(`${value}
9684
+ `);
9685
+ }
9686
+ function writeBinary(stdout, value) {
9687
+ stdout.write(Buffer.from(value));
9688
+ }
9689
+ function getGlobalOptions5(command) {
9690
+ return command.optsWithGlobals();
9691
+ }
9692
+ function emitJson5(stdout, commandName, data, configPath, meta = {}) {
9693
+ writeLine5(
9694
+ stdout,
9695
+ JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath, ...meta }))
9696
+ );
9697
+ }
9698
+ function toConfigurationError5(filePath, error) {
9699
+ if (error instanceof CliError) {
9700
+ throw error;
9701
+ }
9702
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
9703
+ throw new CliError("configuration_error", `Config file not found at ${filePath}.`);
9704
+ }
9705
+ if (error instanceof Error) {
9706
+ throw new CliError("configuration_error", error.message);
9707
+ }
9708
+ throw error;
9709
+ }
9710
+ function createDocumentTemplatesApi(command, filePath) {
9711
+ try {
9712
+ const config = readRuntimeConfig(filePath);
9713
+ const runtime = resolveRuntimeContext(config, getGlobalOptions5(command));
9714
+ if (!runtime.baseUrl) {
9715
+ throw new CliError(
9716
+ "configuration_error",
9717
+ `Resolved profile "${runtime.profile}" is missing a base URL.`
9718
+ );
9719
+ }
9720
+ return {
9721
+ api: new DocumentTemplatesApi(
9722
+ new ApiClient({
9723
+ baseUrl: runtime.baseUrl,
9724
+ apiKey: runtime.apiKey
9725
+ })
9726
+ ),
9727
+ json: runtime.json,
9728
+ readOnly: runtime.readOnly
9729
+ };
9730
+ } catch (error) {
9731
+ toConfigurationError5(filePath, error);
9732
+ }
9733
+ }
9734
+ function ensureWritable4(commandName, readOnly, commandMode) {
9735
+ if (readOnly) {
9736
+ throw new CliError(
9737
+ "command_not_allowed_in_read_only_mode",
9738
+ `Command "${commandName}" is not allowed in read-only mode.`,
9739
+ {
9740
+ commandMode,
9741
+ executionMode: "read-only"
9742
+ }
9743
+ );
9744
+ }
9745
+ }
9746
+ function parseUuidOrSlugSelector4(input) {
9747
+ try {
9748
+ return normalizeUuidOrSlug(input);
9749
+ } catch (error) {
9750
+ const message = error instanceof Error ? error.message : "Invalid selector.";
9751
+ throw new InvalidArgumentError4(message);
9752
+ }
9753
+ }
9754
+ function isJsonObject4(value) {
9755
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9756
+ }
9757
+ function isEmptyObject4(value) {
9758
+ return Object.keys(value).length === 0;
9759
+ }
9760
+ function parseJsonValue2(input, sourceLabel) {
9761
+ try {
9762
+ return JSON.parse(input);
9763
+ } catch (error) {
9764
+ const message = error instanceof Error ? error.message : "Invalid JSON.";
9765
+ throw new CliError(
9766
+ "invalid_cli_usage",
9767
+ `Failed to parse JSON from ${sourceLabel}: ${message}`
9768
+ );
9769
+ }
9770
+ }
9771
+ function readTextFile2(filePath, label) {
9772
+ try {
9773
+ return readFileSync5(filePath, "utf8");
9774
+ } catch (error) {
9775
+ const message = error instanceof Error ? error.message : "Unable to read file.";
9776
+ throw new CliError(
9777
+ "invalid_cli_usage",
9778
+ `Failed to read ${label} from ${filePath}: ${message}`
9779
+ );
9780
+ }
9781
+ }
9782
+ function readJsonObjectOption2(raw, rawFile, rawLabel, rawFileLabel) {
9783
+ if (raw && rawFile) {
9784
+ throw new CliError(
9785
+ "invalid_cli_usage",
9786
+ `Provide either "${rawLabel}" or "${rawFileLabel}", not both.`
9787
+ );
9788
+ }
9789
+ let parsed;
9790
+ if (raw) {
9791
+ parsed = parseJsonValue2(raw, rawLabel);
9792
+ } else if (rawFile) {
9793
+ parsed = parseJsonValue2(readTextFile2(rawFile, rawFileLabel), `${rawFileLabel} ${rawFile}`);
9794
+ } else {
9795
+ return {};
9796
+ }
9797
+ if (!isJsonObject4(parsed)) {
9798
+ throw new CliError(
9799
+ "invalid_cli_usage",
9800
+ `Expected ${raw ? rawLabel : rawFileLabel} to contain a JSON object.`
9801
+ );
9802
+ }
9803
+ return parsed;
9804
+ }
9805
+ function readLocalFile(filePath) {
9806
+ try {
9807
+ return readFileSync5(filePath);
9808
+ } catch (error) {
9809
+ const message = error instanceof Error ? error.message : "Unable to read file.";
9810
+ throw new CliError("invalid_cli_usage", `Failed to read file at ${filePath}: ${message}`);
9811
+ }
9812
+ }
9813
+ function writeDownloadedFile(outputPath, value) {
9814
+ try {
9815
+ writeFileSync2(outputPath, Buffer.from(value));
9816
+ } catch (error) {
9817
+ const message = error instanceof Error ? error.message : "Unable to write file.";
9818
+ throw new CliError(
9819
+ "invalid_cli_usage",
9820
+ `Failed to write downloaded file to ${outputPath}: ${message}`
9821
+ );
9822
+ }
9823
+ }
9824
+ function templateRows(templates) {
9825
+ return templates.map((template) => ({
9826
+ id: template.id ?? "",
9827
+ name: template.name ?? "",
9828
+ assetCount: Array.isArray(template.assets) ? template.assets.length : 0,
9829
+ updatedAt: template.updatedAt ?? ""
9830
+ }));
9831
+ }
9832
+ function resolveTemplate(templates, selector) {
9833
+ if (selector.kind === "uuid") {
9834
+ const match = templates.find((template) => template.id === selector.value);
9835
+ if (match) {
9836
+ return match;
9837
+ }
9838
+ throw new CliError(
9839
+ "invalid_cli_usage",
9840
+ `Document template "${selector.value}" was not found.`
9841
+ );
9842
+ }
9843
+ const matches = templates.filter((template) => template.name === selector.value);
9844
+ if (matches.length === 1) {
9845
+ return matches[0];
9846
+ }
9847
+ if (matches.length > 1) {
9848
+ throw new CliError(
9849
+ "invalid_cli_usage",
9850
+ `Document template "${selector.value}" is ambiguous. Found ${matches.length} matches.`
9851
+ );
9852
+ }
9853
+ throw new CliError(
9854
+ "invalid_cli_usage",
9855
+ `Document template "${selector.value}" was not found.`
9856
+ );
9857
+ }
9858
+ async function resolveTemplateSelector(api, selector) {
9859
+ if (selector.kind === "uuid") {
9860
+ return selector;
9861
+ }
9862
+ const templates = await api.listTemplates();
9863
+ const resolved = resolveTemplate(templates, selector);
9864
+ if (typeof resolved.id !== "string" || resolved.id.length === 0) {
9865
+ throw new CliError(
9866
+ "invalid_cli_usage",
9867
+ `Document template "${selector.value}" is missing an id in the API response.`
9868
+ );
9869
+ }
9870
+ return { kind: "uuid", value: resolved.id };
9871
+ }
9872
+ function buildCreateInput(options) {
9873
+ const raw = readJsonObjectOption2(options.raw, options.rawFile, "--raw", "--raw-file");
9874
+ const input = { ...raw };
9875
+ if (options.name !== void 0) {
9876
+ input.name = options.name;
9877
+ }
9878
+ if (typeof input.name !== "string" || input.name.trim().length === 0) {
9879
+ throw new CliError("invalid_cli_usage", 'Provide template name via "--name".');
9880
+ }
9881
+ if (input.assets === void 0) {
9882
+ input.assets = [];
9883
+ }
9884
+ return input;
9885
+ }
9886
+ function buildUpdateInput(options) {
9887
+ const input = {};
9888
+ if (options.name !== void 0) {
9889
+ input.name = options.name;
9890
+ }
9891
+ if (isEmptyObject4(input)) {
9892
+ throw new CliError("invalid_cli_usage", 'Provide template updates via "--name".');
9893
+ }
9894
+ return input;
9895
+ }
9896
+ function registerDocumentTemplatesCommands(program, options = {}) {
9897
+ const filePath = options.configPath ?? defaultConfigPath;
9898
+ const stdout = getCommandIo5(options.stdout);
9899
+ const templates = program.command("document-templates").description("Manage document templates and their assets");
9900
+ templates.command("list").description("List document templates").action(async function action() {
9901
+ const { api, json } = createDocumentTemplatesApi(this, filePath);
9902
+ const data = await api.listTemplates();
9903
+ if (json) {
9904
+ emitJson5(stdout, "document-templates list", data, filePath);
8347
9905
  return;
8348
9906
  }
8349
- writeLine4(stdout, `Created widget on display "${display.value}".`);
9907
+ printTable(templateRows(data), ["id", "name", "assetCount", "updatedAt"], stdout);
8350
9908
  });
8351
- widgets.command("update").description("Update widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").option("--type <type>").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", (v, p = []) => [...p, v], []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, widgetId, commandOptions) {
8352
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8353
- ensureWritable3("displays widgets update", readOnly, "write");
8354
- const data = await api.updateWidget(display, widgetId, buildWidgetBody(commandOptions));
9909
+ templates.command("get").description("Get a document template by UUID or exact name").argument("<template>", "template UUID or exact name", parseUuidOrSlugSelector4).action(async function action(template) {
9910
+ const { api, json } = createDocumentTemplatesApi(this, filePath);
9911
+ const list = await api.listTemplates();
9912
+ const data = resolveTemplate(list, template);
8355
9913
  if (json) {
8356
- emitJson4(stdout, "displays widgets update", data);
9914
+ emitJson5(stdout, "document-templates get", data, filePath);
8357
9915
  return;
8358
9916
  }
8359
- writeLine4(stdout, `Updated widget "${widgetId}".`);
9917
+ printTable([data], void 0, stdout);
8360
9918
  });
8361
- widgets.command("delete").description("Delete widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").action(async function action(display, widgetId) {
8362
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8363
- ensureWritable3("displays widgets delete", readOnly, "destructive");
8364
- const data = await api.deleteWidget(display, widgetId);
9919
+ templates.command("create").description("Create a document template").option("--name <name>", "template name").option("--raw <json>", "raw create JSON payload").option("--raw-file <path>", "path to raw create JSON payload").action(async function action(commandOptions) {
9920
+ const { api, json, readOnly } = createDocumentTemplatesApi(this, filePath);
9921
+ ensureWritable4("document-templates create", readOnly, "write");
9922
+ const data = await api.createTemplate(buildCreateInput(commandOptions));
8365
9923
  if (json) {
8366
- emitJson4(stdout, "displays widgets delete", data);
9924
+ emitJson5(stdout, "document-templates create", data, filePath);
8367
9925
  return;
8368
9926
  }
8369
- writeLine4(stdout, `Deleted widget "${widgetId}".`);
9927
+ writeLine5(stdout, `Created document template "${String(data.name ?? commandOptions.name)}".`);
8370
9928
  });
8371
- const targets = displays.command("navigation-targets").description("Manage navigation targets");
8372
- targets.command("list").description("List navigation targets").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
8373
- const { api, json } = createDisplaysApi(this, filePath);
8374
- const data = await api.getDisplay(display);
8375
- const items = getDisplaySubresource(data.navigationTargets);
9929
+ templates.command("update").description("Rename a document template").argument("<template>", "template UUID or exact name", parseUuidOrSlugSelector4).option("--name <name>", "new template name").action(async function action(template, commandOptions) {
9930
+ const { api, json, readOnly } = createDocumentTemplatesApi(this, filePath);
9931
+ ensureWritable4("document-templates update", readOnly, "write");
9932
+ const resolved = await resolveTemplateSelector(api, template);
9933
+ const data = await api.updateTemplate(resolved, buildUpdateInput(commandOptions));
8376
9934
  if (json) {
8377
- emitJson4(stdout, "displays navigation-targets list", items);
9935
+ emitJson5(stdout, "document-templates update", data, filePath);
8378
9936
  return;
8379
9937
  }
8380
- printTable(items, void 0, stdout);
9938
+ writeLine5(stdout, `Updated document template "${String(data.name ?? template.value)}".`);
8381
9939
  });
8382
- targets.command("create").description("Create navigation target").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--source-widget <id>").requiredOption("--target-display <id>").requiredOption("--target-variable <id>").option("--label <label>").action(async function action(display, commandOptions) {
8383
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8384
- ensureWritable3("displays navigation-targets create", readOnly, "write");
8385
- const data = await api.createNavigationTarget(display, buildNavigationTargetBody(commandOptions));
9940
+ templates.command("delete").description("Delete a document template (and its assets)").argument("<template>", "template UUID or exact name", parseUuidOrSlugSelector4).action(async function action(template) {
9941
+ const { api, json, readOnly } = createDocumentTemplatesApi(this, filePath);
9942
+ ensureWritable4("document-templates delete", readOnly, "destructive");
9943
+ const resolved = await resolveTemplateSelector(api, template);
9944
+ const data = await api.deleteTemplate(resolved);
8386
9945
  if (json) {
8387
- emitJson4(stdout, "displays navigation-targets create", data);
9946
+ emitJson5(stdout, "document-templates delete", data, filePath);
8388
9947
  return;
8389
9948
  }
8390
- writeLine4(stdout, `Created navigation target on display "${display.value}".`);
9949
+ writeLine5(stdout, `Deleted document template "${template.value}".`);
8391
9950
  });
8392
- targets.command("update").description("Update navigation target").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<target>", "target id").option("--source-widget <id>").option("--target-display <id>").option("--target-variable <id>").option("--label <label>").action(async function action(display, targetId, commandOptions) {
8393
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8394
- ensureWritable3("displays navigation-targets update", readOnly, "write");
8395
- const data = await api.updateNavigationTarget(display, targetId, buildNavigationTargetBody(commandOptions));
8396
- if (json) {
8397
- emitJson4(stdout, "displays navigation-targets update", data);
8398
- return;
9951
+ const assets = templates.command("assets").description("Manage document template assets");
9952
+ assets.command("add").description("Upload an asset file to a document template").argument("<template>", "template UUID or exact name", parseUuidOrSlugSelector4).argument("<path>", "path to the local file").option("--name <name>", "override the asset display name").action(async function action(template, localPath, commandOptions) {
9953
+ const { api, json, readOnly } = createDocumentTemplatesApi(this, filePath);
9954
+ ensureWritable4("document-templates assets add", readOnly, "write");
9955
+ const contents = readLocalFile(localPath);
9956
+ const formData = new FormData();
9957
+ formData.append("file", new Blob([contents]), basename(localPath));
9958
+ if (commandOptions.name !== void 0) {
9959
+ formData.append("name", commandOptions.name);
8399
9960
  }
8400
- writeLine4(stdout, `Updated navigation target "${targetId}".`);
8401
- });
8402
- targets.command("delete").description("Delete navigation target").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<target>", "target id").action(async function action(display, targetId) {
8403
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8404
- ensureWritable3("displays navigation-targets delete", readOnly, "destructive");
8405
- const data = await api.deleteNavigationTarget(display, targetId);
9961
+ const resolved = await resolveTemplateSelector(api, template);
9962
+ const data = await api.addAsset(resolved, formData);
8406
9963
  if (json) {
8407
- emitJson4(stdout, "displays navigation-targets delete", data);
9964
+ emitJson5(stdout, "document-templates assets add", data, filePath);
8408
9965
  return;
8409
9966
  }
8410
- writeLine4(stdout, `Deleted navigation target "${targetId}".`);
9967
+ writeLine5(
9968
+ stdout,
9969
+ `Added asset "${commandOptions.name ?? basename(localPath)}" to template "${template.value}".`
9970
+ );
8411
9971
  });
8412
- const cross = displays.command("cross-widget-filter").description("Manage cross widget filter");
8413
- cross.command("get").description("Get cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
8414
- const { api, json } = createDisplaysApi(this, filePath);
8415
- const data = await api.getDisplay(display);
8416
- const item = data.crossWidgetFilter ?? null;
9972
+ assets.command("delete").description("Delete one asset from a document template").argument("<template>", "template UUID or exact name", parseUuidOrSlugSelector4).argument("<asset>", "asset UUID").action(async function action(template, assetId) {
9973
+ const { api, json, readOnly } = createDocumentTemplatesApi(this, filePath);
9974
+ ensureWritable4("document-templates assets delete", readOnly, "destructive");
9975
+ const resolved = await resolveTemplateSelector(api, template);
9976
+ const data = await api.deleteAsset(resolved, assetId);
8417
9977
  if (json) {
8418
- emitJson4(stdout, "displays cross-widget-filter get", item);
9978
+ emitJson5(stdout, "document-templates assets delete", data, filePath);
8419
9979
  return;
8420
9980
  }
8421
- printTable(item ? [item] : [], void 0, stdout);
9981
+ writeLine5(stdout, `Deleted asset "${assetId}" from template "${template.value}".`);
8422
9982
  });
8423
- cross.command("set").description("Set cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--source-widget <id>").requiredOption("--source-field <id>").requiredOption("--target <widget:field>", "repeatable target", (v, p = []) => [...p, v], []).action(async function action(display, commandOptions) {
8424
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8425
- ensureWritable3("displays cross-widget-filter set", readOnly, "write");
8426
- const data = await api.setCrossWidgetFilter(display, buildCrossWidgetFilterBody(commandOptions));
8427
- if (json) {
8428
- emitJson4(stdout, "displays cross-widget-filter set", data);
8429
- return;
9983
+ assets.command("download").description("Download the raw bytes of a document template asset").argument("<template>", "template UUID or exact name", parseUuidOrSlugSelector4).argument("<asset>", "asset UUID").option("--output <path>", "write the downloaded bytes to a path").action(async function action(template, assetId, commandOptions) {
9984
+ const { api, json } = createDocumentTemplatesApi(this, filePath);
9985
+ if (json && !commandOptions.output) {
9986
+ throw new CliError(
9987
+ "invalid_cli_usage",
9988
+ 'Provide "--output" when using "--json" with "document-templates assets download".'
9989
+ );
8430
9990
  }
8431
- writeLine4(stdout, `Updated cross widget filter for display "${display.value}".`);
8432
- });
8433
- cross.command("delete").description("Delete cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
8434
- const { api, json, readOnly } = createDisplaysApi(this, filePath);
8435
- ensureWritable3("displays cross-widget-filter delete", readOnly, "destructive");
8436
- const data = await api.deleteCrossWidgetFilter(display);
8437
- if (json) {
8438
- emitJson4(stdout, "displays cross-widget-filter delete", data);
9991
+ const resolved = await resolveTemplateSelector(api, template);
9992
+ const data = await api.downloadAsset(resolved, assetId);
9993
+ if (commandOptions.output) {
9994
+ writeDownloadedFile(commandOptions.output, data);
9995
+ if (json) {
9996
+ emitJson5(
9997
+ stdout,
9998
+ "document-templates assets download",
9999
+ {
10000
+ template: resolved.value,
10001
+ asset: assetId,
10002
+ output: commandOptions.output,
10003
+ bytes: data.byteLength
10004
+ },
10005
+ filePath
10006
+ );
10007
+ return;
10008
+ }
10009
+ writeLine5(stdout, `Downloaded asset to "${commandOptions.output}".`);
8439
10010
  return;
8440
10011
  }
8441
- writeLine4(stdout, `Deleted cross widget filter for display "${display.value}".`);
10012
+ writeBinary(stdout, data);
8442
10013
  });
8443
10014
  }
8444
10015
 
8445
10016
  // src/commands/files.ts
8446
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
8447
- import { basename } from "path";
8448
- import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
10017
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
10018
+ import { basename as basename2 } from "path";
10019
+ import { InvalidArgumentError as InvalidArgumentError5 } from "commander";
8449
10020
 
8450
10021
  // src/api/files.ts
8451
- function selectorValue4(selector) {
10022
+ function selectorValue5(selector) {
8452
10023
  return selector.value;
8453
10024
  }
8454
10025
  var FilesApi = class {
@@ -8462,11 +10033,11 @@ var FilesApi = class {
8462
10033
  });
8463
10034
  }
8464
10035
  downloadFile(file) {
8465
- return this.client.requestBinary(`/api/v2/files/${selectorValue4(file)}`);
10036
+ return this.client.requestBinary(`/api/v2/files/${selectorValue5(file)}`);
8466
10037
  }
8467
10038
  deleteFile(file) {
8468
10039
  return this.client.requestJson(
8469
- `/api/v2/files/${selectorValue4(file)}`,
10040
+ `/api/v2/files/${selectorValue5(file)}`,
8470
10041
  { method: "DELETE" }
8471
10042
  );
8472
10043
  }
@@ -8476,26 +10047,26 @@ var FilesApi = class {
8476
10047
  };
8477
10048
 
8478
10049
  // src/commands/files.ts
8479
- function getCommandIo5(stdout) {
10050
+ function getCommandIo6(stdout) {
8480
10051
  return stdout ?? process.stdout;
8481
10052
  }
8482
- function writeLine5(stdout, value) {
10053
+ function writeLine6(stdout, value) {
8483
10054
  stdout.write(`${value}
8484
10055
  `);
8485
10056
  }
8486
- function writeBinary(stdout, value) {
10057
+ function writeBinary2(stdout, value) {
8487
10058
  stdout.write(Buffer.from(value));
8488
10059
  }
8489
- function getGlobalOptions5(command) {
10060
+ function getGlobalOptions6(command) {
8490
10061
  return command.optsWithGlobals();
8491
10062
  }
8492
- function emitJson5(stdout, commandName, data, configPath, meta = {}) {
8493
- writeLine5(
10063
+ function emitJson6(stdout, commandName, data, configPath, meta = {}) {
10064
+ writeLine6(
8494
10065
  stdout,
8495
10066
  JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath, ...meta }))
8496
10067
  );
8497
10068
  }
8498
- function toConfigurationError5(filePath, error) {
10069
+ function toConfigurationError6(filePath, error) {
8499
10070
  if (error instanceof CliError) {
8500
10071
  throw error;
8501
10072
  }
@@ -8510,7 +10081,7 @@ function toConfigurationError5(filePath, error) {
8510
10081
  function createFilesApi(command, filePath) {
8511
10082
  try {
8512
10083
  const config = readRuntimeConfig(filePath);
8513
- const runtime = resolveRuntimeContext(config, getGlobalOptions5(command));
10084
+ const runtime = resolveRuntimeContext(config, getGlobalOptions6(command));
8514
10085
  if (!runtime.baseUrl) {
8515
10086
  throw new CliError(
8516
10087
  "configuration_error",
@@ -8528,10 +10099,10 @@ function createFilesApi(command, filePath) {
8528
10099
  readOnly: runtime.readOnly
8529
10100
  };
8530
10101
  } catch (error) {
8531
- toConfigurationError5(filePath, error);
10102
+ toConfigurationError6(filePath, error);
8532
10103
  }
8533
10104
  }
8534
- function ensureWritable4(commandName, readOnly, commandMode) {
10105
+ function ensureWritable5(commandName, readOnly, commandMode) {
8535
10106
  if (readOnly) {
8536
10107
  throw new CliError(
8537
10108
  "command_not_allowed_in_read_only_mode",
@@ -8543,28 +10114,28 @@ function ensureWritable4(commandName, readOnly, commandMode) {
8543
10114
  );
8544
10115
  }
8545
10116
  }
8546
- function parseUuidOrSlugSelector4(input) {
10117
+ function parseUuidOrSlugSelector5(input) {
8547
10118
  try {
8548
10119
  return normalizeUuidOrSlug(input);
8549
10120
  } catch (error) {
8550
10121
  const message = error instanceof Error ? error.message : "Invalid selector.";
8551
- throw new InvalidArgumentError4(message);
10122
+ throw new InvalidArgumentError5(message);
8552
10123
  }
8553
10124
  }
8554
10125
  function isRecord3(value) {
8555
10126
  return typeof value === "object" && value !== null && !Array.isArray(value);
8556
10127
  }
8557
- function readLocalFile(filePath) {
10128
+ function readLocalFile2(filePath) {
8558
10129
  try {
8559
- return readFileSync5(filePath);
10130
+ return readFileSync6(filePath);
8560
10131
  } catch (error) {
8561
10132
  const message = error instanceof Error ? error.message : "Unable to read file.";
8562
10133
  throw new CliError("invalid_cli_usage", `Failed to read file at ${filePath}: ${message}`);
8563
10134
  }
8564
10135
  }
8565
- function writeDownloadedFile(outputPath, value) {
10136
+ function writeDownloadedFile2(outputPath, value) {
8566
10137
  try {
8567
- writeFileSync2(outputPath, Buffer.from(value));
10138
+ writeFileSync3(outputPath, Buffer.from(value));
8568
10139
  } catch (error) {
8569
10140
  const message = error instanceof Error ? error.message : "Unable to write file.";
8570
10141
  throw new CliError(
@@ -8575,20 +10146,20 @@ function writeDownloadedFile(outputPath, value) {
8575
10146
  }
8576
10147
  function registerFilesCommands(program, options = {}) {
8577
10148
  const filePath = options.configPath ?? defaultConfigPath;
8578
- const stdout = getCommandIo5(options.stdout);
10149
+ const stdout = getCommandIo6(options.stdout);
8579
10150
  const files = program.command("files").description("Manage files and storage");
8580
10151
  files.command("upload").description("Upload a local file").argument("<path>", "path to the local file").action(function action(path) {
8581
10152
  const { api, json, readOnly } = createFilesApi(this, filePath);
8582
- ensureWritable4("files upload", readOnly, "write");
8583
- const contents = readLocalFile(path);
10153
+ ensureWritable5("files upload", readOnly, "write");
10154
+ const contents = readLocalFile2(path);
8584
10155
  const formData = new FormData();
8585
- formData.append("file", new Blob([contents]), basename(path));
10156
+ formData.append("file", new Blob([contents]), basename2(path));
8586
10157
  return api.uploadFile(formData).then((data) => {
8587
10158
  if (json) {
8588
- emitJson5(stdout, "files upload", data, filePath);
10159
+ emitJson6(stdout, "files upload", data, filePath);
8589
10160
  return;
8590
10161
  }
8591
- writeLine5(stdout, `Uploaded file "${basename(path)}".`);
10162
+ writeLine6(stdout, `Uploaded file "${basename2(path)}".`);
8592
10163
  });
8593
10164
  });
8594
10165
  files.command("download").description("Download a file").argument("<file>", "file identifier").option("--output <path>", "write the downloaded file to a path").action(function action(file, commandOptions) {
@@ -8599,12 +10170,12 @@ function registerFilesCommands(program, options = {}) {
8599
10170
  'Provide "--output" when using "--json" with "files download".'
8600
10171
  );
8601
10172
  }
8602
- const fileSelector = parseUuidOrSlugSelector4(file);
10173
+ const fileSelector = parseUuidOrSlugSelector5(file);
8603
10174
  return api.downloadFile(fileSelector).then((data) => {
8604
10175
  if (commandOptions.output) {
8605
- writeDownloadedFile(commandOptions.output, data);
10176
+ writeDownloadedFile2(commandOptions.output, data);
8606
10177
  if (json) {
8607
- emitJson5(
10178
+ emitJson6(
8608
10179
  stdout,
8609
10180
  "files download",
8610
10181
  {
@@ -8616,45 +10187,45 @@ function registerFilesCommands(program, options = {}) {
8616
10187
  );
8617
10188
  return;
8618
10189
  }
8619
- writeLine5(stdout, `Downloaded file to "${commandOptions.output}".`);
10190
+ writeLine6(stdout, `Downloaded file to "${commandOptions.output}".`);
8620
10191
  return;
8621
10192
  }
8622
- writeBinary(stdout, data);
10193
+ writeBinary2(stdout, data);
8623
10194
  });
8624
10195
  });
8625
10196
  files.command("delete").description("Delete a file").argument("<file>", "file identifier").action(function action(file) {
8626
10197
  const { api, json, readOnly } = createFilesApi(this, filePath);
8627
- ensureWritable4("files delete", readOnly, "destructive");
8628
- const fileSelector = parseUuidOrSlugSelector4(file);
10198
+ ensureWritable5("files delete", readOnly, "destructive");
10199
+ const fileSelector = parseUuidOrSlugSelector5(file);
8629
10200
  return api.deleteFile(fileSelector).then((data) => {
8630
10201
  if (json) {
8631
- emitJson5(stdout, "files delete", data, filePath);
10202
+ emitJson6(stdout, "files delete", data, filePath);
8632
10203
  return;
8633
10204
  }
8634
- writeLine5(stdout, `Deleted file "${fileSelector.value}".`);
10205
+ writeLine6(stdout, `Deleted file "${fileSelector.value}".`);
8635
10206
  });
8636
10207
  });
8637
10208
  files.command("storage").description("Show storage usage information").action(function action() {
8638
10209
  const { api, json } = createFilesApi(this, filePath);
8639
10210
  return api.getStorageInfo().then((data) => {
8640
10211
  if (json) {
8641
- emitJson5(stdout, "files storage", data, filePath);
10212
+ emitJson6(stdout, "files storage", data, filePath);
8642
10213
  return;
8643
10214
  }
8644
10215
  if (isRecord3(data)) {
8645
10216
  printTable([data], Object.keys(data), stdout);
8646
10217
  return;
8647
10218
  }
8648
- writeLine5(stdout, String(data));
10219
+ writeLine6(stdout, String(data));
8649
10220
  });
8650
10221
  });
8651
10222
  }
8652
10223
 
8653
10224
  // src/commands/logs.ts
8654
- import { InvalidArgumentError as InvalidArgumentError5 } from "commander";
10225
+ import { InvalidArgumentError as InvalidArgumentError6 } from "commander";
8655
10226
 
8656
10227
  // src/api/logs.ts
8657
- function selectorValue5(selector) {
10228
+ function selectorValue6(selector) {
8658
10229
  return selector.value;
8659
10230
  }
8660
10231
  var LogsApi = class {
@@ -8663,29 +10234,29 @@ var LogsApi = class {
8663
10234
  }
8664
10235
  listLogs(schema, recordId) {
8665
10236
  return this.client.getJson(
8666
- `/api/v2/schemas/${selectorValue5(schema)}/records/${recordId}/logs`
10237
+ `/api/v2/schemas/${selectorValue6(schema)}/records/${recordId}/logs`
8667
10238
  );
8668
10239
  }
8669
10240
  };
8670
10241
 
8671
10242
  // src/commands/logs.ts
8672
- function getCommandIo6(stdout) {
10243
+ function getCommandIo7(stdout) {
8673
10244
  return stdout ?? process.stdout;
8674
10245
  }
8675
- function writeLine6(stdout, value) {
10246
+ function writeLine7(stdout, value) {
8676
10247
  stdout.write(`${value}
8677
10248
  `);
8678
10249
  }
8679
- function getGlobalOptions6(command) {
10250
+ function getGlobalOptions7(command) {
8680
10251
  return command.optsWithGlobals();
8681
10252
  }
8682
- function emitJson6(stdout, commandName, data, ..._extra) {
8683
- writeLine6(
10253
+ function emitJson7(stdout, commandName, data, ..._extra) {
10254
+ writeLine7(
8684
10255
  stdout,
8685
10256
  JSON.stringify(buildJsonSuccess(data, { command: commandName }))
8686
10257
  );
8687
10258
  }
8688
- function toConfigurationError6(filePath, error) {
10259
+ function toConfigurationError7(filePath, error) {
8689
10260
  if (error instanceof CliError) {
8690
10261
  throw error;
8691
10262
  }
@@ -8700,7 +10271,7 @@ function toConfigurationError6(filePath, error) {
8700
10271
  function createLogsApi(command, filePath) {
8701
10272
  try {
8702
10273
  const config = readRuntimeConfig(filePath);
8703
- const runtime = resolveRuntimeContext(config, getGlobalOptions6(command));
10274
+ const runtime = resolveRuntimeContext(config, getGlobalOptions7(command));
8704
10275
  if (!runtime.baseUrl) {
8705
10276
  throw new CliError(
8706
10277
  "configuration_error",
@@ -8717,25 +10288,25 @@ function createLogsApi(command, filePath) {
8717
10288
  json: runtime.json
8718
10289
  };
8719
10290
  } catch (error) {
8720
- toConfigurationError6(filePath, error);
10291
+ toConfigurationError7(filePath, error);
8721
10292
  }
8722
10293
  }
8723
- function parseUuidOrSlugSelector5(input) {
10294
+ function parseUuidOrSlugSelector6(input) {
8724
10295
  try {
8725
10296
  return normalizeUuidOrSlug(input);
8726
10297
  } catch (error) {
8727
10298
  const message = error instanceof Error ? error.message : "Invalid selector.";
8728
- throw new InvalidArgumentError5(message);
10299
+ throw new InvalidArgumentError6(message);
8729
10300
  }
8730
10301
  }
8731
- function isJsonObject4(value) {
10302
+ function isJsonObject5(value) {
8732
10303
  return typeof value === "object" && value !== null && !Array.isArray(value);
8733
10304
  }
8734
10305
  function toTableRows3(data) {
8735
10306
  if (Array.isArray(data)) {
8736
- return data.map((entry) => isJsonObject4(entry) ? entry : { value: entry });
10307
+ return data.map((entry) => isJsonObject5(entry) ? entry : { value: entry });
8737
10308
  }
8738
- if (isJsonObject4(data)) {
10309
+ if (isJsonObject5(data)) {
8739
10310
  return [data];
8740
10311
  }
8741
10312
  return [{ value: data }];
@@ -8745,13 +10316,13 @@ function emitTable3(stdout, data) {
8745
10316
  }
8746
10317
  function registerLogsCommands(program, options = {}) {
8747
10318
  const filePath = options.configPath ?? defaultConfigPath;
8748
- const stdout = getCommandIo6(options.stdout);
10319
+ const stdout = getCommandIo7(options.stdout);
8749
10320
  const logs = program.command("logs").description("Inspect record change logs");
8750
- logs.command("list").description("List change logs for a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector5).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector5).action(async function action(schema, record) {
10321
+ logs.command("list").description("List change logs for a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector6).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector6).action(async function action(schema, record) {
8751
10322
  const { api, json } = createLogsApi(this, filePath);
8752
10323
  const data = await api.listLogs(schema, record.value);
8753
10324
  if (json) {
8754
- emitJson6(stdout, "logs list", data, filePath);
10325
+ emitJson7(stdout, "logs list", data, filePath);
8755
10326
  return;
8756
10327
  }
8757
10328
  emitTable3(stdout, data);
@@ -8759,11 +10330,11 @@ function registerLogsCommands(program, options = {}) {
8759
10330
  }
8760
10331
 
8761
10332
  // src/commands/navigation.ts
8762
- import { readFileSync as readFileSync6 } from "fs";
8763
- import { InvalidArgumentError as InvalidArgumentError6 } from "commander";
10333
+ import { readFileSync as readFileSync7 } from "fs";
10334
+ import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
8764
10335
 
8765
10336
  // src/api/navigation.ts
8766
- function jsonOptions4(body, method) {
10337
+ function jsonOptions5(body, method) {
8767
10338
  return {
8768
10339
  method,
8769
10340
  headers: {
@@ -8772,7 +10343,7 @@ function jsonOptions4(body, method) {
8772
10343
  body: JSON.stringify(body)
8773
10344
  };
8774
10345
  }
8775
- function selectorValue6(selector) {
10346
+ function selectorValue7(selector) {
8776
10347
  return selector.value;
8777
10348
  }
8778
10349
  var NavigationApi = class {
@@ -8785,44 +10356,44 @@ var NavigationApi = class {
8785
10356
  createFolder(body) {
8786
10357
  return this.client.requestJson(
8787
10358
  "/api/v2/navigation/folders",
8788
- jsonOptions4(body, "POST")
10359
+ jsonOptions5(body, "POST")
8789
10360
  );
8790
10361
  }
8791
10362
  updateFolder(folder, body) {
8792
10363
  return this.client.requestJson(
8793
- `/api/v2/navigation/folders/${selectorValue6(folder)}`,
8794
- jsonOptions4(body, "PATCH")
10364
+ `/api/v2/navigation/folders/${selectorValue7(folder)}`,
10365
+ jsonOptions5(body, "PATCH")
8795
10366
  );
8796
10367
  }
8797
10368
  deleteFolder(folder) {
8798
10369
  return this.client.requestJson(
8799
- `/api/v2/navigation/folders/${selectorValue6(folder)}`,
10370
+ `/api/v2/navigation/folders/${selectorValue7(folder)}`,
8800
10371
  { method: "DELETE" }
8801
10372
  );
8802
10373
  }
8803
10374
  moveResource(body) {
8804
10375
  return this.client.requestJson(
8805
10376
  "/api/v2/navigation/resources:move",
8806
- jsonOptions4(body, "POST")
10377
+ jsonOptions5(body, "POST")
8807
10378
  );
8808
10379
  }
8809
10380
  };
8810
10381
 
8811
10382
  // src/commands/navigation.ts
8812
- function getCommandIo7(stdout) {
10383
+ function getCommandIo8(stdout) {
8813
10384
  return stdout ?? process.stdout;
8814
10385
  }
8815
- function writeLine7(stdout, value) {
10386
+ function writeLine8(stdout, value) {
8816
10387
  stdout.write(`${value}
8817
10388
  `);
8818
10389
  }
8819
- function getGlobalOptions7(command) {
10390
+ function getGlobalOptions8(command) {
8820
10391
  return command.optsWithGlobals();
8821
10392
  }
8822
- function emitJson7(stdout, commandName, data, configPath) {
8823
- writeLine7(stdout, JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath })));
10393
+ function emitJson8(stdout, commandName, data, configPath) {
10394
+ writeLine8(stdout, JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath })));
8824
10395
  }
8825
- function toConfigurationError7(filePath, error) {
10396
+ function toConfigurationError8(filePath, error) {
8826
10397
  if (error instanceof CliError) {
8827
10398
  throw error;
8828
10399
  }
@@ -8837,7 +10408,7 @@ function toConfigurationError7(filePath, error) {
8837
10408
  function createNavigationApi(command, filePath) {
8838
10409
  try {
8839
10410
  const config = readRuntimeConfig(filePath);
8840
- const runtime = resolveRuntimeContext(config, getGlobalOptions7(command));
10411
+ const runtime = resolveRuntimeContext(config, getGlobalOptions8(command));
8841
10412
  if (!runtime.baseUrl) {
8842
10413
  throw new CliError("configuration_error", `Resolved profile "${runtime.profile}" is missing a base URL.`);
8843
10414
  }
@@ -8852,10 +10423,10 @@ function createNavigationApi(command, filePath) {
8852
10423
  readOnly: runtime.readOnly
8853
10424
  };
8854
10425
  } catch (error) {
8855
- toConfigurationError7(filePath, error);
10426
+ toConfigurationError8(filePath, error);
8856
10427
  }
8857
10428
  }
8858
- function ensureWritable5(commandName, readOnly, mode) {
10429
+ function ensureWritable6(commandName, readOnly, mode) {
8859
10430
  if (readOnly) {
8860
10431
  throw new CliError(
8861
10432
  "command_not_allowed_in_read_only_mode",
@@ -8864,12 +10435,12 @@ function ensureWritable5(commandName, readOnly, mode) {
8864
10435
  );
8865
10436
  }
8866
10437
  }
8867
- function parseUuidOrSlugSelector6(input) {
10438
+ function parseUuidOrSlugSelector7(input) {
8868
10439
  try {
8869
10440
  return normalizeUuidOrSlug(input);
8870
10441
  } catch (error) {
8871
10442
  const message = error instanceof Error ? error.message : "Invalid selector.";
8872
- throw new InvalidArgumentError6(message);
10443
+ throw new InvalidArgumentError7(message);
8873
10444
  }
8874
10445
  }
8875
10446
  function parseInteger(value, flagName) {
@@ -8886,7 +10457,7 @@ function readJsonObject(raw, rawFile) {
8886
10457
  if (raw && rawFile) {
8887
10458
  throw new CliError("invalid_cli_usage", 'Use either "--props" or "--props-file", not both.');
8888
10459
  }
8889
- const source = rawFile ? readFileSync6(rawFile, "utf8") : raw;
10460
+ const source = rawFile ? readFileSync7(rawFile, "utf8") : raw;
8890
10461
  if (!source) {
8891
10462
  return {};
8892
10463
  }
@@ -8948,41 +10519,41 @@ function folderRows2(folders) {
8948
10519
  }
8949
10520
  function registerNavigationCommands(program, options = {}) {
8950
10521
  const filePath = options.configPath ?? defaultConfigPath;
8951
- const stdout = getCommandIo7(options.stdout);
10522
+ const stdout = getCommandIo8(options.stdout);
8952
10523
  const navigation = program.command("navigation").description("Manage workspace navigation");
8953
10524
  const folders = navigation.command("folders").description("Manage navigation folders");
8954
10525
  folders.command("list").description("List navigation folders").action(async function action() {
8955
10526
  const { api, json } = createNavigationApi(this, filePath);
8956
10527
  const data = await api.listFolders();
8957
10528
  if (json) {
8958
- emitJson7(stdout, "navigation folders list", data, filePath);
10529
+ emitJson8(stdout, "navigation folders list", data, filePath);
8959
10530
  return;
8960
10531
  }
8961
10532
  printTable(folderRows2(data), ["id", "order", "name", "resourceCount"], stdout);
8962
10533
  });
8963
- folders.command("get").description("Get a navigation folder by UUID or exact name").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector6).action(async function action(folder) {
10534
+ folders.command("get").description("Get a navigation folder by UUID or exact name").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector7).action(async function action(folder) {
8964
10535
  const { api, json } = createNavigationApi(this, filePath);
8965
10536
  const data = await api.listFolders();
8966
10537
  const resolved = resolveFolder2(data, folder);
8967
10538
  if (json) {
8968
- emitJson7(stdout, "navigation folders get", resolved, filePath);
10539
+ emitJson8(stdout, "navigation folders get", resolved, filePath);
8969
10540
  return;
8970
10541
  }
8971
10542
  printTable(folderRows2([resolved]), ["id", "order", "name", "resourceCount"], stdout);
8972
10543
  });
8973
- folders.command("create").description("Create a navigation folder").requiredOption("--name <name>", "folder name").option("--order <n>", "folder order").option("--props <json>", "folder additionalProperties JSON").option("--props-file <path>", "folder additionalProperties JSON file").action(async function action(commandOptions) {
10544
+ folders.command("create").description("Create a navigation folder").requiredOption("--name <name>", "folder name").option("--order <n>", "display order (lower values appear first)").option("--props <json>", 'additionalProperties JSON: free-form client metadata, e.g. {"color":"blue","icon":"chart-line"}').option("--props-file <path>", "path to a file with the additionalProperties JSON").action(async function action(commandOptions) {
8974
10545
  const { api, json, readOnly } = createNavigationApi(this, filePath);
8975
- ensureWritable5("navigation folders create", readOnly, "write");
10546
+ ensureWritable6("navigation folders create", readOnly, "write");
8976
10547
  const data = await api.createFolder(buildFolderBody(commandOptions));
8977
10548
  if (json) {
8978
- emitJson7(stdout, "navigation folders create", data, filePath);
10549
+ emitJson8(stdout, "navigation folders create", data, filePath);
8979
10550
  return;
8980
10551
  }
8981
- writeLine7(stdout, `Created navigation folder "${String(data.name ?? commandOptions.name)}".`);
10552
+ writeLine8(stdout, `Created navigation folder "${String(data.name ?? commandOptions.name)}".`);
8982
10553
  });
8983
- folders.command("update").description("Update a navigation folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector6).option("--name <name>", "folder name").option("--order <n>", "folder order").option("--props <json>", "folder additionalProperties JSON").option("--props-file <path>", "folder additionalProperties JSON file").action(async function action(folder, commandOptions) {
10554
+ folders.command("update").description("Update a navigation folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector7).option("--name <name>", "folder name").option("--order <n>", "display order (lower values appear first)").option("--props <json>", 'additionalProperties JSON: free-form client metadata, e.g. {"color":"blue","icon":"chart-line"}').option("--props-file <path>", "path to a file with the additionalProperties JSON").action(async function action(folder, commandOptions) {
8984
10555
  const { api, json, readOnly } = createNavigationApi(this, filePath);
8985
- ensureWritable5("navigation folders update", readOnly, "write");
10556
+ ensureWritable6("navigation folders update", readOnly, "write");
8986
10557
  const allFolders = await api.listFolders();
8987
10558
  const resolved = resolveFolder2(allFolders, folder);
8988
10559
  const body = buildFolderBody(commandOptions);
@@ -8991,54 +10562,55 @@ function registerNavigationCommands(program, options = {}) {
8991
10562
  }
8992
10563
  const data = await api.updateFolder({ kind: "uuid", value: resolved.id }, body);
8993
10564
  if (json) {
8994
- emitJson7(stdout, "navigation folders update", data, filePath);
10565
+ emitJson8(stdout, "navigation folders update", data, filePath);
8995
10566
  return;
8996
10567
  }
8997
- writeLine7(stdout, `Updated navigation folder "${String(data.name ?? resolved.name ?? resolved.id)}".`);
10568
+ writeLine8(stdout, `Updated navigation folder "${String(data.name ?? resolved.name ?? resolved.id)}".`);
8998
10569
  });
8999
- folders.command("delete").description("Delete a navigation folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector6).action(async function action(folder) {
10570
+ folders.command("delete").description("Delete a navigation folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector7).action(async function action(folder) {
9000
10571
  const { api, json, readOnly } = createNavigationApi(this, filePath);
9001
- ensureWritable5("navigation folders delete", readOnly, "destructive");
10572
+ ensureWritable6("navigation folders delete", readOnly, "destructive");
9002
10573
  const allFolders = await api.listFolders();
9003
10574
  const resolved = resolveFolder2(allFolders, folder);
9004
10575
  const data = await api.deleteFolder({ kind: "uuid", value: resolved.id });
9005
10576
  if (json) {
9006
- emitJson7(stdout, "navigation folders delete", data, filePath);
10577
+ emitJson8(stdout, "navigation folders delete", data, filePath);
9007
10578
  return;
9008
10579
  }
9009
- writeLine7(stdout, `Deleted navigation folder "${resolved.name ?? resolved.id}".`);
10580
+ writeLine8(stdout, `Deleted navigation folder "${resolved.name ?? resolved.id}".`);
9010
10581
  });
9011
- navigation.command("resources").description("Manage navigation resources").command("move").description("Move a resource into a folder").argument("<resource>", "resource UUID").requiredOption("--type <type>", "resource type").requiredOption("--to <folder>", "target folder UUID or exact name").action(async function action(resourceId, commandOptions) {
10582
+ navigation.command("resources").description("Manage navigation resources").command("move").description(
10583
+ "Move a schema into a navigation folder. Only schemas can be moved (the navigation menu holds databases); schedules and sub-schemas follow their parent schema. Use the root folder UUID 00000000-0000-0000-0000-000000000000 to move it back to the workspace root."
10584
+ ).argument("<resource>", "schema UUID").requiredOption("--to <folder>", "target folder UUID or exact name").action(async function action(resourceId, commandOptions) {
9012
10585
  const { api, json, readOnly } = createNavigationApi(this, filePath);
9013
- ensureWritable5("navigation resources move", readOnly, "write");
10586
+ ensureWritable6("navigation resources move", readOnly, "write");
9014
10587
  const allFolders = await api.listFolders();
9015
- const resolved = resolveFolder2(allFolders, parseUuidOrSlugSelector6(commandOptions.to));
10588
+ const resolved = resolveFolder2(allFolders, parseUuidOrSlugSelector7(commandOptions.to));
9016
10589
  const data = await api.moveResource({
9017
10590
  resourceId,
9018
- resourceType: commandOptions.type.toUpperCase(),
9019
10591
  targetFolderId: resolved.id
9020
10592
  });
9021
10593
  if (json) {
9022
- emitJson7(stdout, "navigation resources move", data, filePath);
10594
+ emitJson8(stdout, "navigation resources move", data, filePath);
9023
10595
  return;
9024
10596
  }
9025
- writeLine7(stdout, `Moved resource "${resourceId}" to "${resolved.name ?? resolved.id}".`);
10597
+ writeLine8(stdout, `Moved resource "${resourceId}" to "${resolved.name ?? resolved.id}".`);
9026
10598
  });
9027
10599
  }
9028
10600
 
9029
10601
  // src/commands/profiles.ts
9030
- function getCommandIo8(stdout) {
10602
+ function getCommandIo9(stdout) {
9031
10603
  return stdout ?? process.stdout;
9032
10604
  }
9033
- function writeLine8(stdout, value) {
10605
+ function writeLine9(stdout, value) {
9034
10606
  stdout.write(`${value}
9035
10607
  `);
9036
10608
  }
9037
- function getGlobalOptions8(command) {
10609
+ function getGlobalOptions9(command) {
9038
10610
  return command.optsWithGlobals();
9039
10611
  }
9040
- function emitJson8(stdout, commandName, data, configPath) {
9041
- writeLine8(
10612
+ function emitJson9(stdout, commandName, data, configPath) {
10613
+ writeLine9(
9042
10614
  stdout,
9043
10615
  JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath }))
9044
10616
  );
@@ -9094,15 +10666,15 @@ function emitProfilesTable(stdout, data) {
9094
10666
  }
9095
10667
  function registerProfilesCommands(program, options = {}) {
9096
10668
  const filePath = options.configPath ?? defaultConfigPath;
9097
- const stdout = getCommandIo8(options.stdout);
10669
+ const stdout = getCommandIo9(options.stdout);
9098
10670
  const profiles = program.command("profiles").description("Manage local CLI profiles");
9099
10671
  profiles.command("list").description("List configured profiles").action(function action() {
9100
10672
  const config = readRequiredConfig(filePath);
9101
10673
  const data = Object.entries(config.profiles).map(
9102
10674
  ([name, profile]) => toDisplayProfile(name, profile, config.defaultProfile)
9103
10675
  );
9104
- if (getGlobalOptions8(this).json) {
9105
- emitJson8(stdout, "profiles list", data, filePath);
10676
+ if (getGlobalOptions9(this).json) {
10677
+ emitJson9(stdout, "profiles list", data, filePath);
9106
10678
  return;
9107
10679
  }
9108
10680
  emitProfilesTable(stdout, data);
@@ -9111,8 +10683,8 @@ function registerProfilesCommands(program, options = {}) {
9111
10683
  const config = readRequiredConfig(filePath);
9112
10684
  const profile = getRequiredProfile(config, profileName);
9113
10685
  const data = toDisplayProfile(profileName, profile, config.defaultProfile);
9114
- if (getGlobalOptions8(this).json) {
9115
- emitJson8(stdout, "profiles get", data, filePath);
10686
+ if (getGlobalOptions9(this).json) {
10687
+ emitJson9(stdout, "profiles get", data, filePath);
9116
10688
  return;
9117
10689
  }
9118
10690
  emitProfilesTable(stdout, [data]);
@@ -9126,11 +10698,11 @@ function registerProfilesCommands(program, options = {}) {
9126
10698
  profile: profileName,
9127
10699
  defaultProfile: nextConfig.defaultProfile
9128
10700
  };
9129
- if (getGlobalOptions8(this).json) {
9130
- emitJson8(stdout, "profiles use", data, filePath);
10701
+ if (getGlobalOptions9(this).json) {
10702
+ emitJson9(stdout, "profiles use", data, filePath);
9131
10703
  return;
9132
10704
  }
9133
- writeLine8(stdout, `Using profile "${profileName}".`);
10705
+ writeLine9(stdout, `Using profile "${profileName}".`);
9134
10706
  });
9135
10707
  profiles.command("add").description("Add a profile to the local config").argument("<profile>", "profile name").requiredOption("--api-key <token>", "API key").option("--base-url <url>", "base URL").action(function action(profileName, commandOptions) {
9136
10708
  const config = loadConfigForAdd(filePath);
@@ -9154,11 +10726,11 @@ function registerProfilesCommands(program, options = {}) {
9154
10726
  nextConfig.profiles[profileName] ?? {},
9155
10727
  nextConfig.defaultProfile
9156
10728
  );
9157
- if (getGlobalOptions8(this).json) {
9158
- emitJson8(stdout, "profiles add", data, filePath);
10729
+ if (getGlobalOptions9(this).json) {
10730
+ emitJson9(stdout, "profiles add", data, filePath);
9159
10731
  return;
9160
10732
  }
9161
- writeLine8(stdout, `Added profile "${profileName}".`);
10733
+ writeLine9(stdout, `Added profile "${profileName}".`);
9162
10734
  });
9163
10735
  profiles.command("update").description("Update an existing profile").argument("<profile>", "profile name").option("--api-key <token>", "API key").option("--base-url <url>", "base URL").action(function action(profileName, commandOptions) {
9164
10736
  if (commandOptions.apiKey === void 0 && commandOptions.baseUrl === void 0) {
@@ -9180,11 +10752,11 @@ function registerProfilesCommands(program, options = {}) {
9180
10752
  nextConfig.profiles[profileName] ?? {},
9181
10753
  nextConfig.defaultProfile
9182
10754
  );
9183
- if (getGlobalOptions8(this).json) {
9184
- emitJson8(stdout, "profiles update", data, filePath);
10755
+ if (getGlobalOptions9(this).json) {
10756
+ emitJson9(stdout, "profiles update", data, filePath);
9185
10757
  return;
9186
10758
  }
9187
- writeLine8(stdout, `Updated profile "${profileName}".`);
10759
+ writeLine9(stdout, `Updated profile "${profileName}".`);
9188
10760
  });
9189
10761
  profiles.command("remove").description("Remove a profile from the local config").argument("<profile>", "profile name").action(function action(profileName) {
9190
10762
  const config = readRequiredConfig(filePath);
@@ -9195,20 +10767,20 @@ function registerProfilesCommands(program, options = {}) {
9195
10767
  profile: profileName,
9196
10768
  defaultProfile: nextConfig.defaultProfile
9197
10769
  };
9198
- if (getGlobalOptions8(this).json) {
9199
- emitJson8(stdout, "profiles remove", data, filePath);
10770
+ if (getGlobalOptions9(this).json) {
10771
+ emitJson9(stdout, "profiles remove", data, filePath);
9200
10772
  return;
9201
10773
  }
9202
- writeLine8(stdout, `Removed profile "${profileName}".`);
10774
+ writeLine9(stdout, `Removed profile "${profileName}".`);
9203
10775
  });
9204
10776
  }
9205
10777
 
9206
10778
  // src/commands/records.ts
9207
- import { readFileSync as readFileSync7 } from "fs";
9208
- import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
10779
+ import { readFileSync as readFileSync8 } from "fs";
10780
+ import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
9209
10781
 
9210
10782
  // src/api/records.ts
9211
- function jsonOptions5(body, method) {
10783
+ function jsonOptions6(body, method) {
9212
10784
  return {
9213
10785
  method,
9214
10786
  headers: {
@@ -9217,7 +10789,7 @@ function jsonOptions5(body, method) {
9217
10789
  body: JSON.stringify(body)
9218
10790
  };
9219
10791
  }
9220
- function selectorValue7(selector) {
10792
+ function selectorValue8(selector) {
9221
10793
  return selector.value;
9222
10794
  }
9223
10795
  var RecordsApi = class {
@@ -9232,7 +10804,7 @@ var RecordsApi = class {
9232
10804
  if (query.offset !== void 0) {
9233
10805
  params.set("offset", String(query.offset));
9234
10806
  }
9235
- const path = `/api/v2/schemas/${selectorValue7(schema)}/records`;
10807
+ const path = `/api/v2/schemas/${selectorValue8(schema)}/records`;
9236
10808
  const queryString = params.toString();
9237
10809
  return this.client.getJson(
9238
10810
  queryString ? `${path}?${queryString}` : path
@@ -9241,36 +10813,36 @@ var RecordsApi = class {
9241
10813
  queryRecords(schema, query) {
9242
10814
  const body = this.normalizeQueryInput(query);
9243
10815
  return this.client.requestJson(
9244
- `/api/v2/schemas/${selectorValue7(schema)}/records:query`,
9245
- jsonOptions5(body, "POST")
10816
+ `/api/v2/schemas/${selectorValue8(schema)}/records:query`,
10817
+ jsonOptions6(body, "POST")
9246
10818
  );
9247
10819
  }
9248
10820
  getRecord(schema, record) {
9249
10821
  return this.client.getJson(
9250
- `/api/v2/schemas/${selectorValue7(schema)}/records/${selectorValue7(record)}`
10822
+ `/api/v2/schemas/${selectorValue8(schema)}/records/${selectorValue8(record)}`
9251
10823
  );
9252
10824
  }
9253
- createRecord(schema, input) {
10825
+ createRecords(schema, records) {
9254
10826
  return this.client.requestJson(
9255
- `/api/v2/schemas/${selectorValue7(schema)}/records`,
9256
- jsonOptions5(input, "POST")
10827
+ `/api/v2/schemas/${selectorValue8(schema)}/records`,
10828
+ jsonOptions6({ records }, "POST")
9257
10829
  );
9258
10830
  }
9259
10831
  updateRecord(schema, record, input) {
9260
10832
  return this.client.requestJson(
9261
- `/api/v2/schemas/${selectorValue7(schema)}/records/${selectorValue7(record)}`,
9262
- jsonOptions5(input, "PATCH")
10833
+ `/api/v2/schemas/${selectorValue8(schema)}/records/${selectorValue8(record)}`,
10834
+ jsonOptions6(input, "PATCH")
9263
10835
  );
9264
10836
  }
9265
- deleteRecord(schema, record) {
10837
+ deleteRecords(schema, recordIds) {
9266
10838
  return this.client.requestJson(
9267
- `/api/v2/schemas/${selectorValue7(schema)}/records/${selectorValue7(record)}`,
9268
- { method: "DELETE" }
10839
+ `/api/v2/schemas/${selectorValue8(schema)}/records:delete`,
10840
+ jsonOptions6({ recordIds }, "POST")
9269
10841
  );
9270
10842
  }
9271
10843
  clearRecords(schema) {
9272
10844
  return this.client.requestJson(
9273
- `/api/v2/schemas/${selectorValue7(schema)}/records:clear`,
10845
+ `/api/v2/schemas/${selectorValue8(schema)}/records:clear`,
9274
10846
  { method: "DELETE" }
9275
10847
  );
9276
10848
  }
@@ -9303,7 +10875,7 @@ var RecordsApi = class {
9303
10875
  };
9304
10876
 
9305
10877
  // src/api/schemas.ts
9306
- function jsonOptions6(body, method) {
10878
+ function jsonOptions7(body, method) {
9307
10879
  return {
9308
10880
  method,
9309
10881
  headers: {
@@ -9312,7 +10884,7 @@ function jsonOptions6(body, method) {
9312
10884
  body: JSON.stringify(body)
9313
10885
  };
9314
10886
  }
9315
- function selectorValue8(selector) {
10887
+ function selectorValue9(selector) {
9316
10888
  return selector.value;
9317
10889
  }
9318
10890
  var SchemasApi = class {
@@ -9323,150 +10895,76 @@ var SchemasApi = class {
9323
10895
  return this.client.getJson("/api/v2/schemas");
9324
10896
  }
9325
10897
  getSchema(schema) {
9326
- return this.client.getJson(`/api/v2/schemas/${selectorValue8(schema)}`);
10898
+ return this.client.getJson(`/api/v2/schemas/${selectorValue9(schema)}`);
9327
10899
  }
9328
10900
  createSchema(input) {
9329
- return this.client.requestJson("/api/v2/schemas", jsonOptions6(input, "POST"));
10901
+ return this.client.requestJson("/api/v2/schemas", jsonOptions7(input, "POST"));
9330
10902
  }
9331
10903
  updateSchema(schema, input) {
9332
10904
  return this.client.requestJson(
9333
- `/api/v2/schemas/${selectorValue8(schema)}`,
9334
- jsonOptions6(input, "PATCH")
9335
- );
9336
- }
9337
- deleteSchema(schema) {
9338
- return this.client.requestJson(
9339
- `/api/v2/schemas/${selectorValue8(schema)}`,
9340
- { method: "DELETE" }
9341
- );
9342
- }
9343
- async listFields(schema) {
9344
- const result = await this.getSchema(schema);
9345
- return Array.isArray(result.fields) ? result.fields : [];
9346
- }
9347
- getField(schema, field) {
9348
- return this.client.getJson(
9349
- `/api/v2/schemas/${selectorValue8(schema)}/fields/${selectorValue8(field)}`
9350
- );
9351
- }
9352
- createField(schema, input) {
9353
- return this.client.requestJson(
9354
- `/api/v2/schemas/${selectorValue8(schema)}/fields`,
9355
- jsonOptions6(input, "POST")
10905
+ `/api/v2/schemas/${selectorValue9(schema)}`,
10906
+ jsonOptions7(input, "PATCH")
9356
10907
  );
9357
10908
  }
9358
- updateField(schema, field, input) {
10909
+ setUniqueFields(schema, input) {
9359
10910
  return this.client.requestJson(
9360
- `/api/v2/schemas/${selectorValue8(schema)}/fields/${selectorValue8(field)}`,
9361
- jsonOptions6(input, "PATCH")
10911
+ `/api/v2/schemas/${selectorValue9(schema)}/unique-fields`,
10912
+ jsonOptions7(input, "PUT")
9362
10913
  );
9363
10914
  }
9364
- deleteField(schema, field) {
10915
+ deleteSchema(schema) {
9365
10916
  return this.client.requestJson(
9366
- `/api/v2/schemas/${selectorValue8(schema)}/fields/${selectorValue8(field)}`,
9367
- { method: "DELETE" }
9368
- );
9369
- }
9370
- };
9371
-
9372
- // src/parsers/expressions.ts
9373
- var supportedComparators = /* @__PURE__ */ new Set([
9374
- "eq",
9375
- "neq",
9376
- "gt",
9377
- "gte",
9378
- "lt",
9379
- "lte",
9380
- "contains",
9381
- "in",
9382
- "is_empty",
9383
- "is_not_empty"
9384
- ]);
9385
- var noValueComparators = /* @__PURE__ */ new Set(["is_empty", "is_not_empty"]);
9386
- function parseColonExpression(input) {
9387
- const parts = input.split(":");
9388
- if (parts.length < 2) {
9389
- throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
9390
- }
9391
- const field = parts[0]?.trim();
9392
- const comparator = parts[1]?.trim();
9393
- if (!field) {
9394
- throw new Error(`Expected a field before ":", got "${input}".`);
9395
- }
9396
- if (!comparator) {
9397
- throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
9398
- }
9399
- return [field, comparator, parts.slice(2).join(":").trim()];
9400
- }
9401
- function parseSpaceExpression(input) {
9402
- const parts = input.trim().split(/\s+/);
9403
- if (parts.length < 2) {
9404
- throw new Error(`Expected a field comparator expression, got "${input}".`);
9405
- }
9406
- const field = parts[0];
9407
- const comparator = parts[1];
9408
- if (!field) {
9409
- throw new Error(`Expected a field comparator expression, got "${input}".`);
10917
+ `/api/v2/schemas/${selectorValue9(schema)}`,
10918
+ { method: "DELETE" }
10919
+ );
9410
10920
  }
9411
- if (!comparator) {
9412
- throw new Error(`Expected a comparator after "${field}", got "${input}".`);
10921
+ async listFields(schema) {
10922
+ const result = await this.getSchema(schema);
10923
+ return Array.isArray(result.fields) ? result.fields : [];
9413
10924
  }
9414
- return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
9415
- }
9416
- function normalizeValue(comparator, rawValue) {
9417
- if (noValueComparators.has(comparator)) {
9418
- if (rawValue.length > 0) {
9419
- throw new Error(`Comparator "${comparator}" does not accept a value.`);
9420
- }
9421
- return null;
10925
+ getField(schema, field) {
10926
+ return this.client.getJson(
10927
+ `/api/v2/schemas/${selectorValue9(schema)}/fields/${selectorValue9(field)}`
10928
+ );
9422
10929
  }
9423
- if (comparator === "in") {
9424
- const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
9425
- if (values.length === 0) {
9426
- throw new Error(`Comparator "${comparator}" requires at least one value.`);
9427
- }
9428
- return values;
10930
+ createField(schema, input) {
10931
+ return this.client.requestJson(
10932
+ `/api/v2/schemas/${selectorValue9(schema)}/fields`,
10933
+ jsonOptions7(input, "POST")
10934
+ );
9429
10935
  }
9430
- if (rawValue.length === 0) {
9431
- throw new Error(`Comparator "${comparator}" requires a value.`);
10936
+ updateField(schema, field, input) {
10937
+ return this.client.requestJson(
10938
+ `/api/v2/schemas/${selectorValue9(schema)}/fields/${selectorValue9(field)}`,
10939
+ jsonOptions7(input, "PATCH")
10940
+ );
9432
10941
  }
9433
- return rawValue;
9434
- }
9435
- function parseExpression(input) {
9436
- const trimmed = input.trim();
9437
- const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ") ? parseColonExpression(trimmed) : parseSpaceExpression(trimmed);
9438
- if (!supportedComparators.has(comparatorCandidate)) {
9439
- throw new Error(
9440
- `Unsupported comparator "${comparatorCandidate}" in expression "${input}".`
10942
+ deleteField(schema, field) {
10943
+ return this.client.requestJson(
10944
+ `/api/v2/schemas/${selectorValue9(schema)}/fields/${selectorValue9(field)}`,
10945
+ { method: "DELETE" }
9441
10946
  );
9442
10947
  }
9443
- const comparator = comparatorCandidate;
9444
- return {
9445
- field,
9446
- comparator,
9447
- value: normalizeValue(comparator, rawValue),
9448
- raw: trimmed
9449
- };
9450
- }
10948
+ };
9451
10949
 
9452
10950
  // src/commands/records.ts
9453
- function getCommandIo9(stdout) {
10951
+ function getCommandIo10(stdout) {
9454
10952
  return stdout ?? process.stdout;
9455
10953
  }
9456
- function writeLine9(stdout, value) {
10954
+ function writeLine10(stdout, value) {
9457
10955
  stdout.write(`${value}
9458
10956
  `);
9459
10957
  }
9460
- function getGlobalOptions9(command) {
10958
+ function getGlobalOptions10(command) {
9461
10959
  return command.optsWithGlobals();
9462
10960
  }
9463
- function emitJson9(stdout, commandName, data, configPath, meta = {}) {
9464
- writeLine9(
10961
+ function emitJson10(stdout, commandName, data, configPath, meta = {}) {
10962
+ writeLine10(
9465
10963
  stdout,
9466
10964
  JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath, ...meta }))
9467
10965
  );
9468
10966
  }
9469
- function toConfigurationError8(filePath, error) {
10967
+ function toConfigurationError9(filePath, error) {
9470
10968
  if (error instanceof CliError) {
9471
10969
  throw error;
9472
10970
  }
@@ -9481,7 +10979,7 @@ function toConfigurationError8(filePath, error) {
9481
10979
  function createRecordsApi(command, filePath) {
9482
10980
  try {
9483
10981
  const config = readRuntimeConfig(filePath);
9484
- const runtime = resolveRuntimeContext(config, getGlobalOptions9(command));
10982
+ const runtime = resolveRuntimeContext(config, getGlobalOptions10(command));
9485
10983
  if (!runtime.baseUrl) {
9486
10984
  throw new CliError(
9487
10985
  "configuration_error",
@@ -9500,10 +10998,10 @@ function createRecordsApi(command, filePath) {
9500
10998
  readOnly: runtime.readOnly
9501
10999
  };
9502
11000
  } catch (error) {
9503
- toConfigurationError8(filePath, error);
11001
+ toConfigurationError9(filePath, error);
9504
11002
  }
9505
11003
  }
9506
- function ensureWritable6(commandName, readOnly, commandMode) {
11004
+ function ensureWritable7(commandName, readOnly, commandMode) {
9507
11005
  if (readOnly) {
9508
11006
  throw new CliError(
9509
11007
  "command_not_allowed_in_read_only_mode",
@@ -9515,24 +11013,24 @@ function ensureWritable6(commandName, readOnly, commandMode) {
9515
11013
  );
9516
11014
  }
9517
11015
  }
9518
- function parseUuidOrSlugSelector7(input) {
11016
+ function parseUuidOrSlugSelector8(input) {
9519
11017
  try {
9520
11018
  return normalizeUuidOrSlug(input);
9521
11019
  } catch (error) {
9522
11020
  const message = error instanceof Error ? error.message : "Invalid selector.";
9523
- throw new InvalidArgumentError7(message);
11021
+ throw new InvalidArgumentError8(message);
9524
11022
  }
9525
11023
  }
9526
11024
  function parseNonNegativeInteger2(input) {
9527
11025
  if (!/^\d+$/.test(input.trim())) {
9528
- throw new InvalidArgumentError7(`Expected a non-negative integer, received "${input}".`);
11026
+ throw new InvalidArgumentError8(`Expected a non-negative integer, received "${input}".`);
9529
11027
  }
9530
11028
  return Number(input);
9531
11029
  }
9532
11030
  function parseCommaSeparatedValues(input, label) {
9533
11031
  const values = input.split(",").map((value) => value.trim()).filter(Boolean);
9534
11032
  if (values.length === 0) {
9535
- throw new InvalidArgumentError7(`Expected ${label} to contain at least one value.`);
11033
+ throw new InvalidArgumentError8(`Expected ${label} to contain at least one value.`);
9536
11034
  }
9537
11035
  return values;
9538
11036
  }
@@ -9541,7 +11039,7 @@ function parseExpressionValue(input) {
9541
11039
  return parseExpression(input);
9542
11040
  } catch (error) {
9543
11041
  const message = error instanceof Error ? error.message : "Invalid expression.";
9544
- throw new InvalidArgumentError7(message);
11042
+ throw new InvalidArgumentError8(message);
9545
11043
  }
9546
11044
  }
9547
11045
  function parseKeyValueAssignment(input) {
@@ -9549,7 +11047,7 @@ function parseKeyValueAssignment(input) {
9549
11047
  return parseKeyValue(input);
9550
11048
  } catch (error) {
9551
11049
  const message = error instanceof Error ? error.message : "Invalid assignment.";
9552
- throw new InvalidArgumentError7(message);
11050
+ throw new InvalidArgumentError8(message);
9553
11051
  }
9554
11052
  }
9555
11053
  function parseJsonKeyValueAssignment(input) {
@@ -9557,14 +11055,75 @@ function parseJsonKeyValueAssignment(input) {
9557
11055
  return parseJsonAssignment(input);
9558
11056
  } catch (error) {
9559
11057
  const message = error instanceof Error ? error.message : "Invalid JSON assignment.";
9560
- throw new InvalidArgumentError7(message);
11058
+ throw new InvalidArgumentError8(message);
11059
+ }
11060
+ }
11061
+ function dedupeStrings(values) {
11062
+ return [...new Set(values)];
11063
+ }
11064
+ function toRecordUuid(value) {
11065
+ try {
11066
+ const normalized = normalizeUuidOrSlug(value);
11067
+ return normalized.kind === "uuid" ? normalized.value : null;
11068
+ } catch {
11069
+ return null;
11070
+ }
11071
+ }
11072
+ function collectRecordIds(value, previous = []) {
11073
+ const uuid = toRecordUuid(value.trim());
11074
+ if (!uuid) {
11075
+ throw new InvalidArgumentError8(`Expected a record UUID, received "${value}".`);
11076
+ }
11077
+ return [...previous, uuid];
11078
+ }
11079
+ function readRecordIdsFromFile(path) {
11080
+ let contents;
11081
+ try {
11082
+ contents = readFileSync8(path, "utf8");
11083
+ } catch (error) {
11084
+ const message = error instanceof Error ? error.message : "Unable to read file.";
11085
+ throw new CliError(
11086
+ "invalid_cli_usage",
11087
+ `Failed to read record ids from ${path}: ${message}`
11088
+ );
11089
+ }
11090
+ const ids = [];
11091
+ contents.split(/\r?\n/).forEach((line, index) => {
11092
+ const value = line.trim();
11093
+ if (!value) {
11094
+ return;
11095
+ }
11096
+ const uuid = toRecordUuid(value);
11097
+ if (!uuid) {
11098
+ throw new CliError(
11099
+ "invalid_cli_usage",
11100
+ `Invalid record UUID "${value}" at ${path}:${index + 1}.`
11101
+ );
11102
+ }
11103
+ ids.push(uuid);
11104
+ });
11105
+ return ids;
11106
+ }
11107
+ function resolveDeleteRecordIds(options) {
11108
+ const flagIds = options.record ?? [];
11109
+ const fileIds = options.recordsFile ? readRecordIdsFromFile(options.recordsFile) : [];
11110
+ const recordIds = dedupeStrings([...flagIds, ...fileIds]);
11111
+ if (recordIds.length === 0) {
11112
+ throw new CliError(
11113
+ "invalid_cli_usage",
11114
+ 'Provide at least one record id via "--record <uuid>" (repeatable) or "--records-file <path>".'
11115
+ );
9561
11116
  }
11117
+ return recordIds;
9562
11118
  }
9563
11119
  function collectStringValues(value, previous = []) {
9564
11120
  return [...previous, value];
9565
11121
  }
11122
+ function collectSelectValues(value, previous = []) {
11123
+ return dedupeStrings([...previous, ...parseCommaSeparatedValues(value, "--select")]);
11124
+ }
9566
11125
  function collectSortValues(value, previous = []) {
9567
- return [...previous, ...parseCommaSeparatedValues(value, "--sort")];
11126
+ return dedupeStrings([...previous, ...parseCommaSeparatedValues(value, "--sort")]);
9568
11127
  }
9569
11128
  function collectWhereExpressions(value, previous = []) {
9570
11129
  return [...previous, parseExpressionValue(value)];
@@ -9575,7 +11134,7 @@ function collectKeyValueAssignments(value, previous = []) {
9575
11134
  function collectJsonAssignments(value, previous = []) {
9576
11135
  return [...previous, parseJsonKeyValueAssignment(value)];
9577
11136
  }
9578
- function isJsonObject5(value) {
11137
+ function isJsonObject6(value) {
9579
11138
  return typeof value === "object" && value !== null && !Array.isArray(value);
9580
11139
  }
9581
11140
  function parseRawJsonObject2(input, sourceLabel) {
@@ -9589,7 +11148,7 @@ function parseRawJsonObject2(input, sourceLabel) {
9589
11148
  `Failed to parse JSON from ${sourceLabel}: ${message}`
9590
11149
  );
9591
11150
  }
9592
- if (!isJsonObject5(parsed)) {
11151
+ if (!isJsonObject6(parsed)) {
9593
11152
  throw new CliError(
9594
11153
  "invalid_cli_usage",
9595
11154
  `Expected ${sourceLabel} to contain a JSON object.`
@@ -9610,7 +11169,7 @@ function readRawObject3(raw, rawFile) {
9610
11169
  if (rawFile) {
9611
11170
  let contents;
9612
11171
  try {
9613
- contents = readFileSync7(rawFile, "utf8");
11172
+ contents = readFileSync8(rawFile, "utf8");
9614
11173
  } catch (error) {
9615
11174
  const message = error instanceof Error ? error.message : "Unable to read file.";
9616
11175
  throw new CliError(
@@ -9622,7 +11181,7 @@ function readRawObject3(raw, rawFile) {
9622
11181
  }
9623
11182
  return {};
9624
11183
  }
9625
- function isEmptyObject4(value) {
11184
+ function isEmptyObject5(value) {
9626
11185
  return Object.keys(value).length === 0;
9627
11186
  }
9628
11187
  function buildAssignmentObject(assignments = [], jsonAssignments = []) {
@@ -9654,6 +11213,9 @@ function buildRecordQueryInput(options, allowRaw) {
9654
11213
  if (where && where.length > 0) {
9655
11214
  input.where = where;
9656
11215
  }
11216
+ if (options.search !== void 0) {
11217
+ input.search = { term: options.search };
11218
+ }
9657
11219
  if (options.sort && options.sort.length > 0) {
9658
11220
  input.sort = [...options.sort];
9659
11221
  }
@@ -9665,53 +11227,80 @@ function buildRecordQueryInput(options, allowRaw) {
9665
11227
  }
9666
11228
  return input;
9667
11229
  }
9668
- function buildRecordCreateInput(options) {
9669
- const values = {
9670
- ...readRawObject3(options.raw, options.rawFile),
9671
- ...buildAssignmentObject(options.set, options.setJson)
9672
- };
9673
- if (isEmptyObject4(values)) {
11230
+ function readRecordsFromFile(path) {
11231
+ let contents;
11232
+ try {
11233
+ contents = readFileSync8(path, "utf8");
11234
+ } catch (error) {
11235
+ const message = error instanceof Error ? error.message : "Unable to read file.";
9674
11236
  throw new CliError(
9675
11237
  "invalid_cli_usage",
9676
- 'Provide record fields via "--set", "--set-json", "--raw", or "--raw-file".'
11238
+ `Failed to read records from ${path}: ${message}`
9677
11239
  );
9678
11240
  }
9679
- return { values };
9680
- }
9681
- function readRawSetPayload2(raw) {
9682
- const setValue = raw.set;
9683
- if (setValue === void 0) {
9684
- return {};
11241
+ let parsed;
11242
+ try {
11243
+ parsed = JSON.parse(contents);
11244
+ } catch (error) {
11245
+ const message = error instanceof Error ? error.message : "Invalid JSON.";
11246
+ throw new CliError(
11247
+ "invalid_cli_usage",
11248
+ `Failed to parse JSON from --records-file ${path}: ${message}`
11249
+ );
9685
11250
  }
9686
- if (!isJsonObject5(setValue)) {
11251
+ if (!Array.isArray(parsed)) {
9687
11252
  throw new CliError(
9688
11253
  "invalid_cli_usage",
9689
- 'Expected raw update payload field "set" to be a JSON object.'
11254
+ `Expected --records-file ${path} to contain a JSON array of record objects.`
9690
11255
  );
9691
11256
  }
9692
- return { ...setValue };
11257
+ if (parsed.length === 0) {
11258
+ throw new CliError(
11259
+ "invalid_cli_usage",
11260
+ `--records-file ${path} must contain at least one record.`
11261
+ );
11262
+ }
11263
+ return parsed.map((record, index) => {
11264
+ if (!isJsonObject6(record)) {
11265
+ throw new CliError(
11266
+ "invalid_cli_usage",
11267
+ `Record at index ${index} in --records-file ${path} must be a JSON object.`
11268
+ );
11269
+ }
11270
+ return record;
11271
+ });
9693
11272
  }
9694
- function readRawUnsetPayload2(raw) {
9695
- const unsetValue = raw.unset;
9696
- if (unsetValue === void 0) {
9697
- return [];
11273
+ function buildCreateRecords(options) {
11274
+ const hasSingleRecordFlags = (options.set?.length ?? 0) > 0 || (options.setJson?.length ?? 0) > 0 || options.raw !== void 0 || options.rawFile !== void 0;
11275
+ if (options.recordsFile !== void 0) {
11276
+ if (hasSingleRecordFlags) {
11277
+ throw new CliError(
11278
+ "invalid_cli_usage",
11279
+ 'Provide either "--records-file" or the single-record flags (--set/--set-json/--raw/--raw-file), not both.'
11280
+ );
11281
+ }
11282
+ return readRecordsFromFile(options.recordsFile);
9698
11283
  }
9699
- if (!Array.isArray(unsetValue) || !unsetValue.every((value) => typeof value === "string")) {
11284
+ const values = {
11285
+ ...readRawObject3(options.raw, options.rawFile),
11286
+ ...buildAssignmentObject(options.set, options.setJson)
11287
+ };
11288
+ if (isEmptyObject5(values)) {
9700
11289
  throw new CliError(
9701
11290
  "invalid_cli_usage",
9702
- 'Expected raw update payload field "unset" to be an array of strings.'
11291
+ 'Provide record fields via "--set", "--set-json", "--raw", "--raw-file", or load multiple records with "--records-file".'
9703
11292
  );
9704
11293
  }
9705
- return [...unsetValue];
11294
+ return [values];
9706
11295
  }
9707
11296
  function buildRecordUpdateInput(options) {
9708
11297
  const raw = readRawObject3(options.raw, options.rawFile);
9709
- const input = { ...raw };
11298
+ const input = {};
9710
11299
  const setPayload = {
9711
- ...readRawSetPayload2(raw),
11300
+ ...raw,
9712
11301
  ...buildAssignmentObject(options.set, options.setJson)
9713
11302
  };
9714
- const unsetPayload = [...readRawUnsetPayload2(raw), ...options.unset ?? []];
11303
+ const unsetPayload = [...options.unset ?? []];
9715
11304
  for (const key of Object.keys(setPayload)) {
9716
11305
  const unsetIndex = unsetPayload.indexOf(key);
9717
11306
  if (unsetIndex !== -1) {
@@ -9728,7 +11317,7 @@ function buildRecordUpdateInput(options) {
9728
11317
  } else {
9729
11318
  delete input.unset;
9730
11319
  }
9731
- if (isEmptyObject4(input)) {
11320
+ if (isEmptyObject5(input)) {
9732
11321
  throw new CliError(
9733
11322
  "invalid_cli_usage",
9734
11323
  'Provide record updates via "--set", "--set-json", "--unset", "--raw", or "--raw-file".'
@@ -9738,9 +11327,9 @@ function buildRecordUpdateInput(options) {
9738
11327
  }
9739
11328
  function toTableRows4(data) {
9740
11329
  if (Array.isArray(data)) {
9741
- return data.map((entry) => isJsonObject5(entry) ? entry : { value: entry });
11330
+ return data.map((entry) => isJsonObject6(entry) ? entry : { value: entry });
9742
11331
  }
9743
- if (isJsonObject5(data)) {
11332
+ if (isJsonObject6(data)) {
9744
11333
  return [data];
9745
11334
  }
9746
11335
  return [{ value: data }];
@@ -9770,9 +11359,9 @@ async function resolveSchemaSelector(api, selector) {
9770
11359
  }
9771
11360
  function registerRecordsCommands(program, options = {}) {
9772
11361
  const filePath = options.configPath ?? defaultConfigPath;
9773
- const stdout = getCommandIo9(options.stdout);
11362
+ const stdout = getCommandIo10(options.stdout);
9774
11363
  const records = program.command("records").description("Manage schema records");
9775
- records.command("list").description("List records for a schema with simple pagination").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).option("--limit <number>", "limit results", parseNonNegativeInteger2).option("--offset <number>", "offset results", parseNonNegativeInteger2).action(async function action(schema, commandOptions) {
11364
+ records.command("list").description("List records for a schema with simple pagination").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--limit <number>", "limit results", parseNonNegativeInteger2).option("--offset <number>", "offset results", parseNonNegativeInteger2).action(async function action(schema, commandOptions) {
9776
11365
  const { api, schemasApi, json } = createRecordsApi(this, filePath);
9777
11366
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9778
11367
  const data = await api.listRecords(
@@ -9781,7 +11370,7 @@ function registerRecordsCommands(program, options = {}) {
9781
11370
  );
9782
11371
  const recordsData = getQueryRecords(data);
9783
11372
  if (json) {
9784
- emitJson9(
11373
+ emitJson10(
9785
11374
  stdout,
9786
11375
  "records list",
9787
11376
  recordsData,
@@ -9792,7 +11381,10 @@ function registerRecordsCommands(program, options = {}) {
9792
11381
  }
9793
11382
  emitTable4(stdout, recordsData);
9794
11383
  });
9795
- records.command("query").description("Query records for a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).option("--select <fields>", "comma-separated field selection", (value) => parseCommaSeparatedValues(value, "--select")).option("--where <expression>", "where expression", collectWhereExpressions, []).option("--sort <field:direction>", "sort field(s)", collectSortValues, []).option("--limit <number>", "limit results", parseNonNegativeInteger2).option("--offset <number>", "offset results", parseNonNegativeInteger2).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, commandOptions) {
11384
+ records.command("query").description("Query records for a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--select <fields>", "field selection (comma-separated, repeatable)", collectSelectValues, []).option("--where <expression>", "where expression", collectWhereExpressions, []).option(
11385
+ "--search <term>",
11386
+ "free-text search term (matched across the schema's searchable fields)"
11387
+ ).option("--sort <field:direction>", "sort field(s)", collectSortValues, []).option("--limit <number>", "limit results", parseNonNegativeInteger2).option("--offset <number>", "offset results", parseNonNegativeInteger2).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, commandOptions) {
9796
11388
  const { api, schemasApi, json } = createRecordsApi(this, filePath);
9797
11389
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9798
11390
  const data = await api.queryRecords(
@@ -9801,7 +11393,7 @@ function registerRecordsCommands(program, options = {}) {
9801
11393
  );
9802
11394
  const recordsData = getQueryRecords(data);
9803
11395
  if (json) {
9804
- emitJson9(
11396
+ emitJson10(
9805
11397
  stdout,
9806
11398
  "records query",
9807
11399
  recordsData,
@@ -9812,33 +11404,50 @@ function registerRecordsCommands(program, options = {}) {
9812
11404
  }
9813
11405
  emitTable4(stdout, recordsData);
9814
11406
  });
9815
- records.command("get").description("Get a record by UUID or slug").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector7).action(async function action(schema, record) {
11407
+ records.command("get").description("Get a record by UUID or slug").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema, record) {
9816
11408
  const { api, schemasApi, json } = createRecordsApi(this, filePath);
9817
11409
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9818
11410
  const data = await api.getRecord(resolvedSchema, record);
9819
11411
  if (json) {
9820
- emitJson9(stdout, "records get", data, filePath);
11412
+ emitJson10(stdout, "records get", data, filePath);
9821
11413
  return;
9822
11414
  }
9823
11415
  emitTable4(stdout, data);
9824
11416
  });
9825
- records.command("create").description("Create a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).option("--set <field=value>", "set a string field", collectKeyValueAssignments, []).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, commandOptions) {
11417
+ records.command("create").description("Create one or more records").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option(
11418
+ "--set <field=value>",
11419
+ "set a string field (reference fields: a record id, 'recordId::label', or an exact display value)",
11420
+ collectKeyValueAssignments,
11421
+ []
11422
+ ).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").option(
11423
+ "--records-file <path>",
11424
+ "path to a JSON array of record objects (mutually exclusive with --set/--set-json/--raw/--raw-file)"
11425
+ ).action(async function action(schema, commandOptions) {
9826
11426
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
9827
- ensureWritable6("records create", readOnly, "write");
11427
+ ensureWritable7("records create", readOnly, "write");
11428
+ const records2 = buildCreateRecords(commandOptions);
9828
11429
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9829
- const data = await api.createRecord(
9830
- resolvedSchema,
9831
- buildRecordCreateInput(commandOptions)
9832
- );
11430
+ const data = await api.createRecords(resolvedSchema, records2);
9833
11431
  if (json) {
9834
- emitJson9(stdout, "records create", data, filePath);
11432
+ emitJson10(stdout, "records create", data, filePath);
9835
11433
  return;
9836
11434
  }
9837
- writeLine9(stdout, `Created record "${getRecordIdentifier(data)}".`);
11435
+ writeLine10(stdout, `Created ${data.created.length} record(s) in schema "${schema.value}".`);
11436
+ if (data.errors.length > 0) {
11437
+ writeLine10(stdout, `${data.errors.length} record(s) rejected:`);
11438
+ for (const error of data.errors) {
11439
+ writeLine10(stdout, ` - index ${error.index}: ${error.message}`);
11440
+ }
11441
+ }
9838
11442
  });
9839
- records.command("update").description("Update a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector7).option("--set <field=value>", "set a string field", collectKeyValueAssignments, []).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--unset <field>", "unset a field", collectStringValues, []).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, record, commandOptions) {
11443
+ records.command("update").description("Update a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector8).option(
11444
+ "--set <field=value>",
11445
+ "set a string field (reference fields: a record id, 'recordId::label', or an exact display value)",
11446
+ collectKeyValueAssignments,
11447
+ []
11448
+ ).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--unset <field>", "unset a field", collectStringValues, []).option("--raw <json>", "raw JSON object of fields to set").option("--raw-file <path>", "path to a JSON object of fields to set").action(async function action(schema, record, commandOptions) {
9840
11449
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
9841
- ensureWritable6("records update", readOnly, "write");
11450
+ ensureWritable7("records update", readOnly, "write");
9842
11451
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9843
11452
  const data = await api.updateRecord(
9844
11453
  resolvedSchema,
@@ -9846,60 +11455,71 @@ function registerRecordsCommands(program, options = {}) {
9846
11455
  buildRecordUpdateInput(commandOptions)
9847
11456
  );
9848
11457
  if (json) {
9849
- emitJson9(stdout, "records update", data, filePath);
11458
+ emitJson10(stdout, "records update", data, filePath);
9850
11459
  return;
9851
11460
  }
9852
- writeLine9(stdout, `Updated record "${getRecordIdentifier(data)}".`);
11461
+ writeLine10(stdout, `Updated record "${getRecordIdentifier(data)}".`);
9853
11462
  });
9854
- records.command("delete").description("Delete a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector7).action(async function action(schema, record) {
11463
+ records.command("delete").description("Delete one or more records").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--record <uuid>", "record uuid to delete (repeatable)", collectRecordIds, []).option(
11464
+ "--records-file <path>",
11465
+ "path to a file with one record uuid per line (merged with --record)"
11466
+ ).action(async function action(schema, commandOptions) {
9855
11467
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
9856
- ensureWritable6("records delete", readOnly, "destructive");
11468
+ ensureWritable7("records delete", readOnly, "destructive");
11469
+ const recordIds = resolveDeleteRecordIds(commandOptions);
9857
11470
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9858
- await api.deleteRecord(resolvedSchema, record);
9859
- const data = {
9860
- schema: resolvedSchema.value,
9861
- record: record.value,
9862
- deleted: true
9863
- };
11471
+ const data = await api.deleteRecords(resolvedSchema, recordIds);
9864
11472
  if (json) {
9865
- emitJson9(stdout, "records delete", data, filePath);
11473
+ emitJson10(stdout, "records delete", data, filePath);
9866
11474
  return;
9867
11475
  }
9868
- writeLine9(stdout, `Deleted record "${record.value}" from schema "${schema.value}".`);
11476
+ writeLine10(
11477
+ stdout,
11478
+ `Deleted ${recordIds.length} record(s) from schema "${schema.value}".`
11479
+ );
9869
11480
  });
9870
- records.command("clear").description("Delete all records from a schema's table").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector7).action(async function action(schema) {
11481
+ records.command("clear").description("Delete all records from a schema's table").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema) {
9871
11482
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
9872
- ensureWritable6("records clear", readOnly, "destructive");
11483
+ ensureWritable7("records clear", readOnly, "destructive");
9873
11484
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
9874
11485
  const data = await api.clearRecords(resolvedSchema);
9875
11486
  if (json) {
9876
- emitJson9(stdout, "records clear", data, filePath);
11487
+ emitJson10(stdout, "records clear", data, filePath);
9877
11488
  return;
9878
11489
  }
9879
- writeLine9(stdout, `Cleared all records from schema "${schema.value}".`);
11490
+ writeLine10(stdout, `Cleared all records from schema "${schema.value}".`);
9880
11491
  });
9881
11492
  }
9882
11493
 
9883
11494
  // src/commands/schemas.ts
9884
- import { readFileSync as readFileSync8 } from "fs";
9885
- import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
9886
- function getCommandIo10(stdout) {
11495
+ import { readFileSync as readFileSync9 } from "fs";
11496
+ import { InvalidArgumentError as InvalidArgumentError9 } from "commander";
11497
+ var HIGHLIGHTED_FIELD_HELP = `
11498
+ Highlighted field:
11499
+ The schema's "main" field. The frontend uses its value as each record's
11500
+ display name, in two places:
11501
+ - Activity panel: the record's title in the activity side panel
11502
+ (the panel with the record's logs and comments).
11503
+ - Detail view: the record's name in the left-hand record list you
11504
+ browse to open each record's form.
11505
+ `;
11506
+ function getCommandIo11(stdout) {
9887
11507
  return stdout ?? process.stdout;
9888
11508
  }
9889
- function writeLine10(stdout, value) {
11509
+ function writeLine11(stdout, value) {
9890
11510
  stdout.write(`${value}
9891
11511
  `);
9892
11512
  }
9893
- function getGlobalOptions10(command) {
11513
+ function getGlobalOptions11(command) {
9894
11514
  return command.optsWithGlobals();
9895
11515
  }
9896
- function emitJson10(stdout, commandName, data, configPath) {
9897
- writeLine10(
11516
+ function emitJson11(stdout, commandName, data, configPath) {
11517
+ writeLine11(
9898
11518
  stdout,
9899
11519
  JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath }))
9900
11520
  );
9901
11521
  }
9902
- function toConfigurationError9(filePath, error) {
11522
+ function toConfigurationError10(filePath, error) {
9903
11523
  if (error instanceof CliError) {
9904
11524
  throw error;
9905
11525
  }
@@ -9914,7 +11534,7 @@ function toConfigurationError9(filePath, error) {
9914
11534
  function createSchemasApi(command, filePath) {
9915
11535
  try {
9916
11536
  const config = readRuntimeConfig(filePath);
9917
- const runtime = resolveRuntimeContext(config, getGlobalOptions10(command));
11537
+ const runtime = resolveRuntimeContext(config, getGlobalOptions11(command));
9918
11538
  if (!runtime.baseUrl) {
9919
11539
  throw new CliError(
9920
11540
  "configuration_error",
@@ -9933,10 +11553,10 @@ function createSchemasApi(command, filePath) {
9933
11553
  readOnly: runtime.readOnly
9934
11554
  };
9935
11555
  } catch (error) {
9936
- toConfigurationError9(filePath, error);
11556
+ toConfigurationError10(filePath, error);
9937
11557
  }
9938
11558
  }
9939
- function ensureWritable7(commandName, readOnly, commandMode) {
11559
+ function ensureWritable8(commandName, readOnly, commandMode) {
9940
11560
  if (readOnly) {
9941
11561
  throw new CliError(
9942
11562
  "command_not_allowed_in_read_only_mode",
@@ -9948,14 +11568,17 @@ function ensureWritable7(commandName, readOnly, commandMode) {
9948
11568
  );
9949
11569
  }
9950
11570
  }
9951
- function parseUuidOrSlugSelector8(input) {
11571
+ function parseUuidOrSlugSelector9(input) {
9952
11572
  try {
9953
11573
  return normalizeUuidOrSlug(input);
9954
11574
  } catch (error) {
9955
11575
  const message = error instanceof Error ? error.message : "Invalid selector.";
9956
- throw new InvalidArgumentError8(message);
11576
+ throw new InvalidArgumentError9(message);
9957
11577
  }
9958
11578
  }
11579
+ function collectFieldSelectors(value, previous = []) {
11580
+ return [...previous, parseUuidOrSlugSelector9(value)];
11581
+ }
9959
11582
  function parseBooleanFlag(input) {
9960
11583
  const normalized = input.trim().toLowerCase();
9961
11584
  if (["true", "1", "yes", "y", "on"].includes(normalized)) {
@@ -9964,9 +11587,9 @@ function parseBooleanFlag(input) {
9964
11587
  if (["false", "0", "no", "n", "off"].includes(normalized)) {
9965
11588
  return false;
9966
11589
  }
9967
- throw new InvalidArgumentError8(`Expected a boolean value, received "${input}".`);
11590
+ throw new InvalidArgumentError9(`Expected a boolean value, received "${input}".`);
9968
11591
  }
9969
- function isJsonObject6(value) {
11592
+ function isJsonObject7(value) {
9970
11593
  return typeof value === "object" && value !== null && !Array.isArray(value);
9971
11594
  }
9972
11595
  function parseRawJsonObject3(input, sourceLabel) {
@@ -9980,7 +11603,7 @@ function parseRawJsonObject3(input, sourceLabel) {
9980
11603
  `Failed to parse JSON from ${sourceLabel}: ${message}`
9981
11604
  );
9982
11605
  }
9983
- if (!isJsonObject6(parsed)) {
11606
+ if (!isJsonObject7(parsed)) {
9984
11607
  throw new CliError(
9985
11608
  "invalid_cli_usage",
9986
11609
  `Expected ${sourceLabel} to contain a JSON object.`
@@ -10001,7 +11624,7 @@ function readRawObject4(raw, rawFile) {
10001
11624
  if (rawFile) {
10002
11625
  let contents;
10003
11626
  try {
10004
- contents = readFileSync8(rawFile, "utf8");
11627
+ contents = readFileSync9(rawFile, "utf8");
10005
11628
  } catch (error) {
10006
11629
  const message = error instanceof Error ? error.message : "Unable to read file.";
10007
11630
  throw new CliError(
@@ -10060,7 +11683,7 @@ function applyFieldDefaultValue(input, defaultValue) {
10060
11683
  return;
10061
11684
  }
10062
11685
  const config = input.config;
10063
- if (config !== void 0 && !isJsonObject6(config)) {
11686
+ if (config !== void 0 && !isJsonObject7(config)) {
10064
11687
  throw new CliError(
10065
11688
  "invalid_cli_usage",
10066
11689
  'Expected field "config" to be a JSON object when using "--default".'
@@ -10100,7 +11723,7 @@ function buildFieldCreateInput(options) {
10100
11723
  input.required = options.required;
10101
11724
  }
10102
11725
  applyFieldDefaultValue(input, options.default);
10103
- if ("config" in input && isJsonObject6(input.config) && isEmptyObject5(input.config)) {
11726
+ if ("config" in input && isJsonObject7(input.config) && isEmptyObject6(input.config)) {
10104
11727
  delete input.config;
10105
11728
  }
10106
11729
  return input;
@@ -10173,35 +11796,35 @@ async function resolveFieldSelector(api, schema, selector) {
10173
11796
  value: match.id
10174
11797
  };
10175
11798
  }
10176
- function isEmptyObject5(value) {
11799
+ function isEmptyObject6(value) {
10177
11800
  return Object.keys(value).length === 0;
10178
11801
  }
10179
11802
  function registerSchemasCommands(program, options = {}) {
10180
11803
  const filePath = options.configPath ?? defaultConfigPath;
10181
- const stdout = getCommandIo10(options.stdout);
11804
+ const stdout = getCommandIo11(options.stdout);
10182
11805
  const schemas = program.command("schemas").description("Manage schemas and schema fields");
10183
11806
  schemas.command("list").description("List schemas").action(async function action() {
10184
11807
  const { api, json } = createSchemasApi(this, filePath);
10185
11808
  const data = await api.listSchemas();
10186
11809
  if (json) {
10187
- emitJson10(stdout, "schemas list", data, filePath);
11810
+ emitJson11(stdout, "schemas list", data, filePath);
10188
11811
  return;
10189
11812
  }
10190
11813
  emitSchemasTable(stdout, data);
10191
11814
  });
10192
- schemas.command("get").description("Get a schema by UUID or slug").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema) {
11815
+ schemas.command("get").description("Get a schema by UUID or slug").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).action(async function action(schema) {
10193
11816
  const { api, json } = createSchemasApi(this, filePath);
10194
11817
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10195
11818
  const data = await api.getSchema(resolvedSchema);
10196
11819
  if (json) {
10197
- emitJson10(stdout, "schemas get", data, filePath);
11820
+ emitJson11(stdout, "schemas get", data, filePath);
10198
11821
  return;
10199
11822
  }
10200
11823
  emitSchemasTable(stdout, [data]);
10201
11824
  });
10202
11825
  schemas.command("create").description("Create a schema").requiredOption("--name <name>", "schema name").requiredOption("--slug <slug>", "schema slug").option("--auditable", "mark schema as auditable").option("--commentable", "mark schema as commentable").action(async function action(commandOptions) {
10203
11826
  const { api, json, readOnly } = createSchemasApi(this, filePath);
10204
- ensureWritable7("schemas create", readOnly, "write");
11827
+ ensureWritable8("schemas create", readOnly, "write");
10205
11828
  const input = {
10206
11829
  name: commandOptions.name,
10207
11830
  slug: commandOptions.slug,
@@ -10210,32 +11833,87 @@ function registerSchemasCommands(program, options = {}) {
10210
11833
  };
10211
11834
  const data = await api.createSchema(input);
10212
11835
  if (json) {
10213
- emitJson10(stdout, "schemas create", data, filePath);
11836
+ emitJson11(stdout, "schemas create", data, filePath);
10214
11837
  return;
10215
11838
  }
10216
- writeLine10(stdout, `Created schema "${data.slug ?? commandOptions.slug}".`);
11839
+ writeLine11(stdout, `Created schema "${data.slug ?? commandOptions.slug}".`);
10217
11840
  });
10218
- schemas.command("update").description("Update a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--name <name>", "schema name").option("--slug <slug>", "schema slug").option("--auditable <bool>", "auditable flag", parseBooleanFlag).option("--commentable <bool>", "commentable flag", parseBooleanFlag).action(async function action(schema, commandOptions) {
11841
+ schemas.command("update").description("Update a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).option("--name <name>", "schema name").option("--slug <slug>", "schema slug").option("--auditable <bool>", "auditable flag", parseBooleanFlag).option("--commentable <bool>", "commentable flag", parseBooleanFlag).option(
11842
+ "--highlighted-field <field>",
11843
+ "its value is used as each record's display name in the UI (field uuid or slug)",
11844
+ parseUuidOrSlugSelector9
11845
+ ).action(async function action(schema, commandOptions) {
10219
11846
  const { api, json, readOnly } = createSchemasApi(this, filePath);
10220
- ensureWritable7("schemas update", readOnly, "write");
11847
+ ensureWritable8("schemas update", readOnly, "write");
10221
11848
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10222
11849
  const input = buildSchemaUpdateInput(commandOptions);
10223
- if (isEmptyObject5(input)) {
11850
+ if (commandOptions.highlightedField !== void 0) {
11851
+ const resolvedField = await resolveFieldSelector(
11852
+ api,
11853
+ resolvedSchema,
11854
+ commandOptions.highlightedField
11855
+ );
11856
+ input.highlightedField = resolvedField.value;
11857
+ }
11858
+ if (isEmptyObject6(input)) {
10224
11859
  throw new CliError(
10225
11860
  "invalid_cli_usage",
10226
- 'Provide at least one schema field to update via "--name", "--slug", "--auditable", or "--commentable".'
11861
+ 'Provide at least one schema field to update via "--name", "--slug", "--auditable", "--commentable", or "--highlighted-field".'
10227
11862
  );
10228
11863
  }
10229
11864
  const data = await api.updateSchema(resolvedSchema, input);
10230
11865
  if (json) {
10231
- emitJson10(stdout, "schemas update", data, filePath);
11866
+ emitJson11(stdout, "schemas update", data, filePath);
11867
+ return;
11868
+ }
11869
+ writeLine11(stdout, `Updated schema "${data.slug ?? schema.value}".`);
11870
+ }).addHelpText("after", HIGHLIGHTED_FIELD_HELP);
11871
+ schemas.command("set-unique-fields").description("Set or clear the composite unique-field constraint on a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).option(
11872
+ "--field <field>",
11873
+ "field uuid or slug to include in the unique constraint (repeatable)",
11874
+ collectFieldSelectors,
11875
+ []
11876
+ ).option("--clear", "clear the unique constraint (sends an empty field list)").action(async function action(schema, commandOptions) {
11877
+ const { api, json, readOnly } = createSchemasApi(this, filePath);
11878
+ ensureWritable8("schemas set-unique-fields", readOnly, "write");
11879
+ const selectors = commandOptions.field ?? [];
11880
+ if (commandOptions.clear && selectors.length > 0) {
11881
+ throw new CliError(
11882
+ "invalid_cli_usage",
11883
+ 'Use either "--field" or "--clear", not both.'
11884
+ );
11885
+ }
11886
+ if (!commandOptions.clear && selectors.length === 0) {
11887
+ throw new CliError(
11888
+ "invalid_cli_usage",
11889
+ 'Provide at least one field via "--field", or pass "--clear" to remove the unique constraint.'
11890
+ );
11891
+ }
11892
+ const resolvedSchema = await resolveSchemaSelector2(api, schema);
11893
+ const resolvedFields = await Promise.all(
11894
+ selectors.map((selector) => resolveFieldSelector(api, resolvedSchema, selector))
11895
+ );
11896
+ const input = {
11897
+ fieldIds: resolvedFields.map((field) => field.value)
11898
+ };
11899
+ const data = await api.setUniqueFields(resolvedSchema, input);
11900
+ if (json) {
11901
+ emitJson11(stdout, "schemas set-unique-fields", data, filePath);
11902
+ return;
11903
+ }
11904
+ if (input.fieldIds.length === 0) {
11905
+ writeLine11(stdout, `Cleared unique fields on schema "${schema.value}".`);
10232
11906
  return;
10233
11907
  }
10234
- writeLine10(stdout, `Updated schema "${data.slug ?? schema.value}".`);
11908
+ const count = input.fieldIds.length;
11909
+ writeLine11(
11910
+ stdout,
11911
+ `Set unique fields on schema "${schema.value}" (${count} field${count === 1 ? "" : "s"}).`
11912
+ );
10235
11913
  });
10236
- schemas.command("delete").description("Delete a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema) {
11914
+ schemas.command("delete").description("Delete a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).action(async function action(schema) {
10237
11915
  const { api, json, readOnly } = createSchemasApi(this, filePath);
10238
- ensureWritable7("schemas delete", readOnly, "destructive");
11916
+ ensureWritable8("schemas delete", readOnly, "destructive");
10239
11917
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10240
11918
  await api.deleteSchema(resolvedSchema);
10241
11919
  const data = {
@@ -10243,36 +11921,36 @@ function registerSchemasCommands(program, options = {}) {
10243
11921
  deleted: true
10244
11922
  };
10245
11923
  if (json) {
10246
- emitJson10(stdout, "schemas delete", data, filePath);
11924
+ emitJson11(stdout, "schemas delete", data, filePath);
10247
11925
  return;
10248
11926
  }
10249
- writeLine10(stdout, `Deleted schema "${schema.value}".`);
11927
+ writeLine11(stdout, `Deleted schema "${schema.value}".`);
10250
11928
  });
10251
11929
  const fields = schemas.command("fields").description("Manage schema fields");
10252
- fields.command("list").description("List fields for a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema) {
11930
+ fields.command("list").description("List fields for a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).action(async function action(schema) {
10253
11931
  const { api, json } = createSchemasApi(this, filePath);
10254
11932
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10255
11933
  const data = await api.listFields(resolvedSchema);
10256
11934
  if (json) {
10257
- emitJson10(stdout, "schemas fields list", data, filePath);
11935
+ emitJson11(stdout, "schemas fields list", data, filePath);
10258
11936
  return;
10259
11937
  }
10260
11938
  emitFieldsTable(stdout, data);
10261
11939
  });
10262
- fields.command("get").description("Get a field by UUID or slug").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<field>", "field uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema, field) {
11940
+ fields.command("get").description("Get a field by UUID or slug").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).argument("<field>", "field uuid or slug", parseUuidOrSlugSelector9).action(async function action(schema, field) {
10263
11941
  const { api, json } = createSchemasApi(this, filePath);
10264
11942
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10265
11943
  const resolvedField = await resolveFieldSelector(api, resolvedSchema, field);
10266
11944
  const data = await api.getField(resolvedSchema, resolvedField);
10267
11945
  if (json) {
10268
- emitJson10(stdout, "schemas fields get", data, filePath);
11946
+ emitJson11(stdout, "schemas fields get", data, filePath);
10269
11947
  return;
10270
11948
  }
10271
11949
  emitFieldsTable(stdout, [data]);
10272
11950
  });
10273
- fields.command("create").description(`Create a field in a schema.${buildSchemaFieldCreateHelpText()}`).argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--type <type>", `field type (${formatSchemaFieldTypes()})`).option("--slug <slug>", "field slug").option("--label <label>", "field label").option("--required <bool>", "required flag", parseBooleanFlag).option("--default <value>", "default field value").option("--raw <json>", "raw JSON object payload (see type-specific shapes in --help)").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, commandOptions) {
11951
+ fields.command("create").description(`Create a field in a schema.${buildSchemaFieldCreateHelpText()}`).argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).option("--type <type>", `field type (${formatSchemaFieldTypes()})`).option("--slug <slug>", "field slug").option("--label <label>", "field label").option("--required <bool>", "required flag", parseBooleanFlag).option("--default <value>", "default field value").option("--raw <json>", "raw JSON object payload (see type-specific shapes in --help)").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, commandOptions) {
10274
11952
  const { api, json, readOnly } = createSchemasApi(this, filePath);
10275
- ensureWritable7("schemas fields create", readOnly, "write");
11953
+ ensureWritable8("schemas fields create", readOnly, "write");
10276
11954
  const input = buildFieldCreateInput(commandOptions);
10277
11955
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10278
11956
  let data;
@@ -10282,14 +11960,14 @@ function registerSchemasCommands(program, options = {}) {
10282
11960
  toFieldCreateError(error, input);
10283
11961
  }
10284
11962
  if (json) {
10285
- emitJson10(stdout, "schemas fields create", data, filePath);
11963
+ emitJson11(stdout, "schemas fields create", data, filePath);
10286
11964
  return;
10287
11965
  }
10288
- writeLine10(stdout, `Created field "${data.slug ?? commandOptions.slug}".`);
11966
+ writeLine11(stdout, `Created field "${data.slug ?? commandOptions.slug}".`);
10289
11967
  });
10290
- fields.command("update").description("Update a field in a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<field>", "field uuid or slug", parseUuidOrSlugSelector8).option("--label <label>", "field label").option("--required <bool>", "required flag", parseBooleanFlag).option("--default <value>", "default field value").option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, field, commandOptions) {
11968
+ fields.command("update").description("Update a field in a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).argument("<field>", "field uuid or slug", parseUuidOrSlugSelector9).option("--label <label>", "field label").option("--required <bool>", "required flag", parseBooleanFlag).option("--default <value>", "default field value").option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, field, commandOptions) {
10291
11969
  const { api, json, readOnly } = createSchemasApi(this, filePath);
10292
- ensureWritable7("schemas fields update", readOnly, "write");
11970
+ ensureWritable8("schemas fields update", readOnly, "write");
10293
11971
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10294
11972
  const resolvedField = await resolveFieldSelector(api, resolvedSchema, field);
10295
11973
  const currentField = await api.getField(resolvedSchema, resolvedField);
@@ -10298,7 +11976,7 @@ function registerSchemasCommands(program, options = {}) {
10298
11976
  throw new CliError("invalid_cli_usage", `Field "${field.value}" is missing type metadata.`);
10299
11977
  }
10300
11978
  const input = buildFieldUpdateInput(currentType, commandOptions);
10301
- if (isEmptyObject5(input)) {
11979
+ if (isEmptyObject6(input)) {
10302
11980
  throw new CliError(
10303
11981
  "invalid_cli_usage",
10304
11982
  'Provide at least one field update via "--label", "--required", "--raw", or "--raw-file".'
@@ -10306,14 +11984,14 @@ function registerSchemasCommands(program, options = {}) {
10306
11984
  }
10307
11985
  const data = await api.updateField(resolvedSchema, resolvedField, input);
10308
11986
  if (json) {
10309
- emitJson10(stdout, "schemas fields update", data, filePath);
11987
+ emitJson11(stdout, "schemas fields update", data, filePath);
10310
11988
  return;
10311
11989
  }
10312
- writeLine10(stdout, `Updated field "${data.slug ?? field.value}".`);
11990
+ writeLine11(stdout, `Updated field "${data.slug ?? field.value}".`);
10313
11991
  });
10314
- fields.command("delete").description("Delete a field from a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<field>", "field uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema, field) {
11992
+ fields.command("delete").description("Delete a field from a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).argument("<field>", "field uuid or slug", parseUuidOrSlugSelector9).action(async function action(schema, field) {
10315
11993
  const { api, json, readOnly } = createSchemasApi(this, filePath);
10316
- ensureWritable7("schemas fields delete", readOnly, "destructive");
11994
+ ensureWritable8("schemas fields delete", readOnly, "destructive");
10317
11995
  const resolvedSchema = await resolveSchemaSelector2(api, schema);
10318
11996
  const resolvedField = await resolveFieldSelector(api, resolvedSchema, field);
10319
11997
  await api.deleteField(resolvedSchema, resolvedField);
@@ -10323,19 +12001,19 @@ function registerSchemasCommands(program, options = {}) {
10323
12001
  deleted: true
10324
12002
  };
10325
12003
  if (json) {
10326
- emitJson10(stdout, "schemas fields delete", data, filePath);
12004
+ emitJson11(stdout, "schemas fields delete", data, filePath);
10327
12005
  return;
10328
12006
  }
10329
- writeLine10(stdout, `Deleted field "${field.value}" from schema "${schema.value}".`);
12007
+ writeLine11(stdout, `Deleted field "${field.value}" from schema "${schema.value}".`);
10330
12008
  });
10331
12009
  }
10332
12010
 
10333
12011
  // src/commands/teamspace.ts
10334
- import { readFileSync as readFileSync9 } from "fs";
10335
- import { InvalidArgumentError as InvalidArgumentError9 } from "commander";
12012
+ import { readFileSync as readFileSync10 } from "fs";
12013
+ import { InvalidArgumentError as InvalidArgumentError10 } from "commander";
10336
12014
 
10337
12015
  // src/api/teamspace.ts
10338
- function jsonOptions7(body, method) {
12016
+ function jsonOptions8(body, method) {
10339
12017
  return {
10340
12018
  method,
10341
12019
  headers: {
@@ -10344,7 +12022,7 @@ function jsonOptions7(body, method) {
10344
12022
  body: JSON.stringify(body)
10345
12023
  };
10346
12024
  }
10347
- function selectorValue9(selector) {
12025
+ function selectorValue10(selector) {
10348
12026
  return selector.value;
10349
12027
  }
10350
12028
  var TeamspaceApi = class {
@@ -10365,53 +12043,53 @@ var TeamspaceApi = class {
10365
12043
  createFolder(input) {
10366
12044
  return this.client.requestJson(
10367
12045
  "/api/v2/teamspaces/folders",
10368
- jsonOptions7(input, "POST")
12046
+ jsonOptions8(input, "POST")
10369
12047
  );
10370
12048
  }
10371
12049
  updateFolder(folder, input) {
10372
12050
  return this.client.requestJson(
10373
- `/api/v2/teamspaces/folders/${selectorValue9(folder)}`,
10374
- jsonOptions7(input, "PATCH")
12051
+ `/api/v2/teamspaces/folders/${selectorValue10(folder)}`,
12052
+ jsonOptions8(input, "PATCH")
10375
12053
  );
10376
12054
  }
10377
12055
  deleteFolder(folder) {
10378
12056
  return this.client.requestJson(
10379
- `/api/v2/teamspaces/folders/${selectorValue9(folder)}`,
12057
+ `/api/v2/teamspaces/folders/${selectorValue10(folder)}`,
10380
12058
  { method: "DELETE" }
10381
12059
  );
10382
12060
  }
10383
12061
  publishResource(input) {
10384
12062
  return this.client.requestJson(
10385
12063
  "/api/v2/teamspaces/resources:publish",
10386
- jsonOptions7(input, "POST")
12064
+ jsonOptions8(input, "POST")
10387
12065
  );
10388
12066
  }
10389
12067
  unpublishResource(resourceId) {
10390
12068
  return this.client.requestJson(
10391
12069
  "/api/v2/teamspaces/resources:unpublish",
10392
- jsonOptions7({ resourceId }, "POST")
12070
+ jsonOptions8({ resourceId }, "POST")
10393
12071
  );
10394
12072
  }
10395
12073
  };
10396
12074
 
10397
12075
  // src/commands/teamspace.ts
10398
- function getCommandIo11(stdout) {
12076
+ function getCommandIo12(stdout) {
10399
12077
  return stdout ?? process.stdout;
10400
12078
  }
10401
- function writeLine11(stdout, value) {
12079
+ function writeLine12(stdout, value) {
10402
12080
  stdout.write(`${value}
10403
12081
  `);
10404
12082
  }
10405
- function getGlobalOptions11(command) {
12083
+ function getGlobalOptions12(command) {
10406
12084
  return command.optsWithGlobals();
10407
12085
  }
10408
- function emitJson11(stdout, commandName, data, configPath) {
10409
- writeLine11(
12086
+ function emitJson12(stdout, commandName, data, configPath) {
12087
+ writeLine12(
10410
12088
  stdout,
10411
12089
  JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath }))
10412
12090
  );
10413
12091
  }
10414
- function toConfigurationError10(filePath, error) {
12092
+ function toConfigurationError11(filePath, error) {
10415
12093
  if (error instanceof CliError) {
10416
12094
  throw error;
10417
12095
  }
@@ -10426,7 +12104,7 @@ function toConfigurationError10(filePath, error) {
10426
12104
  function createTeamspaceApi(command, filePath) {
10427
12105
  try {
10428
12106
  const config = readRuntimeConfig(filePath);
10429
- const runtime = resolveRuntimeContext(config, getGlobalOptions11(command));
12107
+ const runtime = resolveRuntimeContext(config, getGlobalOptions12(command));
10430
12108
  if (!runtime.baseUrl) {
10431
12109
  throw new CliError(
10432
12110
  "configuration_error",
@@ -10444,10 +12122,10 @@ function createTeamspaceApi(command, filePath) {
10444
12122
  readOnly: runtime.readOnly
10445
12123
  };
10446
12124
  } catch (error) {
10447
- toConfigurationError10(filePath, error);
12125
+ toConfigurationError11(filePath, error);
10448
12126
  }
10449
12127
  }
10450
- function ensureWritable8(commandName, readOnly, commandMode) {
12128
+ function ensureWritable9(commandName, readOnly, commandMode) {
10451
12129
  if (readOnly) {
10452
12130
  throw new CliError(
10453
12131
  "command_not_allowed_in_read_only_mode",
@@ -10459,21 +12137,21 @@ function ensureWritable8(commandName, readOnly, commandMode) {
10459
12137
  );
10460
12138
  }
10461
12139
  }
10462
- function parseUuidOrSlugSelector9(input) {
12140
+ function parseUuidOrSlugSelector10(input) {
10463
12141
  try {
10464
12142
  return normalizeUuidOrSlug(input);
10465
12143
  } catch (error) {
10466
12144
  const message = error instanceof Error ? error.message : "Invalid selector.";
10467
- throw new InvalidArgumentError9(message);
12145
+ throw new InvalidArgumentError10(message);
10468
12146
  }
10469
12147
  }
10470
12148
  function parseNonNegativeInteger3(input) {
10471
12149
  if (!/^\d+$/.test(input.trim())) {
10472
- throw new InvalidArgumentError9(`Expected a non-negative integer, received "${input}".`);
12150
+ throw new InvalidArgumentError10(`Expected a non-negative integer, received "${input}".`);
10473
12151
  }
10474
12152
  return Number(input);
10475
12153
  }
10476
- function isJsonObject7(value) {
12154
+ function isJsonObject8(value) {
10477
12155
  return typeof value === "object" && value !== null && !Array.isArray(value);
10478
12156
  }
10479
12157
  function parseRawJsonObject4(input, sourceLabel) {
@@ -10487,7 +12165,7 @@ function parseRawJsonObject4(input, sourceLabel) {
10487
12165
  `Failed to parse JSON from ${sourceLabel}: ${message}`
10488
12166
  );
10489
12167
  }
10490
- if (!isJsonObject7(parsed)) {
12168
+ if (!isJsonObject8(parsed)) {
10491
12169
  throw new CliError(
10492
12170
  "invalid_cli_usage",
10493
12171
  `Expected ${sourceLabel} to contain a JSON object.`
@@ -10508,7 +12186,7 @@ function readRawObject5(raw, rawFile) {
10508
12186
  if (rawFile) {
10509
12187
  let contents;
10510
12188
  try {
10511
- contents = readFileSync9(rawFile, "utf8");
12189
+ contents = readFileSync10(rawFile, "utf8");
10512
12190
  } catch (error) {
10513
12191
  const message = error instanceof Error ? error.message : "Unable to read file.";
10514
12192
  throw new CliError(
@@ -10520,7 +12198,7 @@ function readRawObject5(raw, rawFile) {
10520
12198
  }
10521
12199
  return void 0;
10522
12200
  }
10523
- function isEmptyObject6(value) {
12201
+ function isEmptyObject7(value) {
10524
12202
  return Object.keys(value).length === 0;
10525
12203
  }
10526
12204
  function buildFolderCreateInput(options) {
@@ -10551,7 +12229,7 @@ function buildFolderUpdateInput(options) {
10551
12229
  if (props !== void 0) {
10552
12230
  input.additionalProperties = props;
10553
12231
  }
10554
- if (isEmptyObject6(input)) {
12232
+ if (isEmptyObject7(input)) {
10555
12233
  throw new CliError(
10556
12234
  "invalid_cli_usage",
10557
12235
  'Provide folder updates via "--name", "--order", "--props", or "--props-file".'
@@ -10561,9 +12239,9 @@ function buildFolderUpdateInput(options) {
10561
12239
  }
10562
12240
  function toTableRows5(data) {
10563
12241
  if (Array.isArray(data)) {
10564
- return data.map((entry) => isJsonObject7(entry) ? entry : { value: entry });
12242
+ return data.map((entry) => isJsonObject8(entry) ? entry : { value: entry });
10565
12243
  }
10566
- if (isJsonObject7(data)) {
12244
+ if (isJsonObject8(data)) {
10567
12245
  return [data];
10568
12246
  }
10569
12247
  return [{ value: data }];
@@ -10625,85 +12303,91 @@ async function resolveFolderSelector2(api, selector) {
10625
12303
  }
10626
12304
  function registerTeamspaceCommands(program, options = {}) {
10627
12305
  const filePath = options.configPath ?? defaultConfigPath;
10628
- const stdout = getCommandIo11(options.stdout);
12306
+ const stdout = getCommandIo12(options.stdout);
10629
12307
  const teamspace = program.command("teamspace").description("Manage teamspace folders and resources");
10630
12308
  const folders = teamspace.command("folders").description("Manage teamspace folders");
10631
- folders.command("list").description("List teamspace folders").option("--include-resources", "include nested resources").action(async function action(commandOptions) {
12309
+ folders.command("list").description("List teamspace folders").option("--include-resources", "include the published resources inside each folder").action(async function action(commandOptions) {
10632
12310
  const { api, json } = createTeamspaceApi(this, filePath);
10633
12311
  const data = await api.listFolders(Boolean(commandOptions.includeResources));
10634
12312
  if (json) {
10635
- emitJson11(stdout, "teamspace folders list", data, filePath);
12313
+ emitJson12(stdout, "teamspace folders list", data, filePath);
10636
12314
  return;
10637
12315
  }
10638
12316
  emitTable5(stdout, data);
10639
12317
  });
10640
- folders.command("get").description("Get a teamspace folder by exact name or UUID").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector9).option("--include-resources", "include nested resources").action(async function action(folder, commandOptions) {
12318
+ folders.command("get").description("Get a teamspace folder by exact name or UUID").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector10).option("--include-resources", "include the published resources inside each folder").action(async function action(folder, commandOptions) {
10641
12319
  const { api, json } = createTeamspaceApi(this, filePath);
10642
12320
  const resolvedFolder = await resolveFolderSelector2(api, folder);
10643
12321
  const allFolders = await api.listFolders(Boolean(commandOptions.includeResources));
10644
12322
  const data = findFolderEntity(allFolders, resolvedFolder);
10645
12323
  if (json) {
10646
- emitJson11(stdout, "teamspace folders get", data, filePath);
12324
+ emitJson12(stdout, "teamspace folders get", data, filePath);
10647
12325
  return;
10648
12326
  }
10649
12327
  emitTable5(stdout, data);
10650
12328
  });
10651
- folders.command("create").description("Create a teamspace folder").requiredOption("--name <name>", "folder name").option("--order <n>", "folder order", parseNonNegativeInteger3).option("--props <json>", "folder properties payload").option("--props-file <path>", "path to a folder properties JSON payload").action(async function action(commandOptions) {
12329
+ folders.command("create").description("Create a teamspace folder").requiredOption("--name <name>", "folder name").option("--order <n>", "display order (lower values appear first)", parseNonNegativeInteger3).option("--props <json>", 'additionalProperties JSON: free-form client metadata, e.g. {"icon":"folder-users","color":"gold"}').option("--props-file <path>", "path to a file with the additionalProperties JSON").action(async function action(commandOptions) {
10652
12330
  const { api, json, readOnly } = createTeamspaceApi(this, filePath);
10653
- ensureWritable8("teamspace folders create", readOnly, "write");
12331
+ ensureWritable9("teamspace folders create", readOnly, "write");
10654
12332
  const data = await api.createFolder(buildFolderCreateInput(commandOptions));
10655
12333
  if (json) {
10656
- emitJson11(stdout, "teamspace folders create", data, filePath);
12334
+ emitJson12(stdout, "teamspace folders create", data, filePath);
10657
12335
  return;
10658
12336
  }
10659
- writeLine11(stdout, `Created folder "${getFolderIdentifier(data)}".`);
12337
+ writeLine12(stdout, `Created folder "${getFolderIdentifier(data)}".`);
10660
12338
  });
10661
- folders.command("update").description("Update a teamspace folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector9).option("--name <name>", "folder name").option("--order <n>", "folder order", parseNonNegativeInteger3).option("--props <json>", "folder properties payload").option("--props-file <path>", "path to a folder properties JSON payload").action(async function action(folder, commandOptions) {
12339
+ folders.command("update").description("Update a teamspace folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector10).option("--name <name>", "folder name").option("--order <n>", "display order (lower values appear first)", parseNonNegativeInteger3).option("--props <json>", 'additionalProperties JSON: free-form client metadata, e.g. {"icon":"folder-users","color":"gold"}').option("--props-file <path>", "path to a file with the additionalProperties JSON").action(async function action(folder, commandOptions) {
10662
12340
  const { api, json, readOnly } = createTeamspaceApi(this, filePath);
10663
- ensureWritable8("teamspace folders update", readOnly, "write");
12341
+ ensureWritable9("teamspace folders update", readOnly, "write");
10664
12342
  const resolvedFolder = await resolveFolderSelector2(api, folder);
10665
12343
  const data = await api.updateFolder(resolvedFolder, buildFolderUpdateInput(commandOptions));
10666
12344
  if (json) {
10667
- emitJson11(stdout, "teamspace folders update", data, filePath);
12345
+ emitJson12(stdout, "teamspace folders update", data, filePath);
10668
12346
  return;
10669
12347
  }
10670
- writeLine11(stdout, `Updated folder "${getFolderIdentifier(data)}".`);
12348
+ writeLine12(stdout, `Updated folder "${getFolderIdentifier(data)}".`);
10671
12349
  });
10672
- folders.command("delete").description("Delete a teamspace folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector9).action(async function action(folder) {
12350
+ folders.command("delete").description("Delete a teamspace folder").argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector10).action(async function action(folder) {
10673
12351
  const { api, json, readOnly } = createTeamspaceApi(this, filePath);
10674
- ensureWritable8("teamspace folders delete", readOnly, "destructive");
12352
+ ensureWritable9("teamspace folders delete", readOnly, "destructive");
10675
12353
  const resolvedFolder = await resolveFolderSelector2(api, folder);
10676
12354
  const data = await api.deleteFolder(resolvedFolder);
10677
12355
  if (json) {
10678
- emitJson11(stdout, "teamspace folders delete", data, filePath);
12356
+ emitJson12(stdout, "teamspace folders delete", data, filePath);
10679
12357
  return;
10680
12358
  }
10681
- writeLine11(stdout, `Deleted folder "${resolvedFolder.value}".`);
12359
+ writeLine12(stdout, `Deleted folder "${resolvedFolder.value}".`);
10682
12360
  });
10683
12361
  const resources = teamspace.command("resources").description("Manage teamspace resources");
10684
- resources.command("publish").description("Publish a resource to a teamspace folder").argument("<resource>", "resource UUID or exact name", parseUuidOrSlugSelector9).requiredOption("--to <folder>", "target folder", parseUuidOrSlugSelector9).action(async function action(resource, commandOptions) {
12362
+ resources.command("publish").description(
12363
+ "Publish a resource to a teamspace folder. Publishing an already-published resource moves it to the new folder."
12364
+ ).argument("<resource>", "schema UUID, schema slug, or display UUID", parseUuidOrSlugSelector10).requiredOption(
12365
+ "--to <folder>",
12366
+ "target folder UUID or exact name (root folder UUID is 00000000-0000-0000-0000-000000000000)",
12367
+ parseUuidOrSlugSelector10
12368
+ ).action(async function action(resource, commandOptions) {
10685
12369
  const { api, json, readOnly } = createTeamspaceApi(this, filePath);
10686
- ensureWritable8("teamspace resources publish", readOnly, "write");
12370
+ ensureWritable9("teamspace resources publish", readOnly, "write");
10687
12371
  const folder = await resolveFolderSelector2(api, commandOptions.to);
10688
12372
  const data = await api.publishResource({
10689
12373
  resourceId: resource.value,
10690
12374
  targetFolderId: folder.value
10691
12375
  });
10692
12376
  if (json) {
10693
- emitJson11(stdout, "teamspace resources publish", data, filePath);
12377
+ emitJson12(stdout, "teamspace resources publish", data, filePath);
10694
12378
  return;
10695
12379
  }
10696
- writeLine11(stdout, `Published resource "${resource.value}" to folder "${folder.value}".`);
12380
+ writeLine12(stdout, `Published resource "${resource.value}" to folder "${folder.value}".`);
10697
12381
  });
10698
- resources.command("unpublish").description("Unpublish a resource from teamspace").argument("<resource>", "resource UUID or exact name", parseUuidOrSlugSelector9).action(async function action(resource) {
12382
+ resources.command("unpublish").description("Unpublish a resource from teamspace (removes it from its shared folder)").argument("<resource>", "schema UUID, schema slug, or display UUID", parseUuidOrSlugSelector10).action(async function action(resource) {
10699
12383
  const { api, json, readOnly } = createTeamspaceApi(this, filePath);
10700
- ensureWritable8("teamspace resources unpublish", readOnly, "write");
12384
+ ensureWritable9("teamspace resources unpublish", readOnly, "write");
10701
12385
  const data = await api.unpublishResource(resource.value);
10702
12386
  if (json) {
10703
- emitJson11(stdout, "teamspace resources unpublish", data, filePath);
12387
+ emitJson12(stdout, "teamspace resources unpublish", data, filePath);
10704
12388
  return;
10705
12389
  }
10706
- writeLine11(stdout, `Unpublished resource "${resource.value}".`);
12390
+ writeLine12(stdout, `Unpublished resource "${resource.value}".`);
10707
12391
  });
10708
12392
  }
10709
12393
 
@@ -10711,12 +12395,13 @@ function registerTeamspaceCommands(program, options = {}) {
10711
12395
  var require2 = createRequire(import.meta.url);
10712
12396
  var { version } = require2("../package.json");
10713
12397
  function buildProgram(options = {}) {
10714
- const program = new Command10();
12398
+ const program = new Command11();
10715
12399
  program.name("zinkee").description("CLI for Zinkee API v2").version(version).enablePositionalOptions().showHelpAfterError().option("--profile <name>", "select a profile from config").option("--base-url <url>", "override the configured base URL").option("--api-key <token>", "override the configured API key").option("--read-only", "run in read-only mode").option("--example", "show examples for a command").option("--json", "emit JSON output").option("--install-completion [shell]", "install shell completion (bash, zsh, fish, powershell)").option("--show-completion [shell]", "print shell completion script for the current shell");
10716
12400
  registerProfilesCommands(program, options);
10717
12401
  registerAutomationsCommands(program, options);
10718
12402
  registerConfigCommands(program, options);
10719
12403
  registerDisplaysCommands(program, options);
12404
+ registerDocumentTemplatesCommands(program, options);
10720
12405
  registerFilesCommands(program, options);
10721
12406
  registerNavigationCommands(program, options);
10722
12407
  registerSchemasCommands(program, options);
@@ -10861,9 +12546,9 @@ function getCompletions(program, request, deps = {}) {
10861
12546
 
10862
12547
  // src/completion/install.ts
10863
12548
  import { execFileSync } from "child_process";
10864
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
12549
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
10865
12550
  import { homedir as homedir2, platform } from "os";
10866
- import { basename as basename2, dirname as dirname2, join as join2 } from "path";
12551
+ import { basename as basename3, dirname as dirname2, join as join2 } from "path";
10867
12552
 
10868
12553
  // src/completion/scripts.ts
10869
12554
  var PROGRAM = "zinkee";
@@ -10985,7 +12670,7 @@ function defaultPowerShellProfile(env) {
10985
12670
  function detectShell(env) {
10986
12671
  const fromEnv = env.shellEnv;
10987
12672
  if (fromEnv) {
10988
- const name = basename2(fromEnv).toLowerCase().replace(/\.exe$/, "");
12673
+ const name = basename3(fromEnv).toLowerCase().replace(/\.exe$/, "");
10989
12674
  if (name === "bash") {
10990
12675
  return "bash";
10991
12676
  }
@@ -11083,7 +12768,7 @@ ${BLOCK_END}
11083
12768
  }
11084
12769
  function readFileOrEmpty(path) {
11085
12770
  try {
11086
- return readFileSync10(path, "utf8");
12771
+ return readFileSync11(path, "utf8");
11087
12772
  } catch (error) {
11088
12773
  if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
11089
12774
  return "";
@@ -11093,7 +12778,7 @@ function readFileOrEmpty(path) {
11093
12778
  }
11094
12779
  function writeFileEnsuringDir(path, content) {
11095
12780
  mkdirSync2(dirname2(path), { recursive: true });
11096
- writeFileSync3(path, content, "utf8");
12781
+ writeFileSync4(path, content, "utf8");
11097
12782
  }
11098
12783
  function installCompletion(shell, env) {
11099
12784
  const target = resolveTarget(shell, env);