zinkee 0.1.18 → 0.1.20

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.
@@ -0,0 +1,47 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test *)",
5
+ "Bash(npm run *)",
6
+ "Bash(pnpm typecheck *)",
7
+ "Bash(pnpm test *)",
8
+ "Bash(npx tsc *)",
9
+ "Read(//home/guillermo/dev/z2-backend/api/src/main/java/com/zinkee/api/domain/model/v2/log/**)",
10
+ "Read(//home/guillermo/dev/z2-backend/**)",
11
+ "Bash(grep *)",
12
+ "Bash(npx vitest *)",
13
+ "Bash(npx tsx *)",
14
+ "Bash(node dist/index.js --help)",
15
+ "Bash(gh repo *)",
16
+ "WebFetch(domain:github.com)",
17
+ "Bash(git config *)",
18
+ "Read(//home/guillermo/.config/**)",
19
+ "Read(//home/guillermo/**)",
20
+ "Bash(curl *)",
21
+ "Bash(node dist/index.js --show-completion zsh)",
22
+ "Bash(node *)",
23
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee\" COMP_CWORD=1 node *)",
24
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas\" COMP_CWORD=2 node *)",
25
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas fields\" COMP_CWORD=3 node dist/index.js)",
26
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas fields cr\" COMP_CWORD=3 node dist/index.js)",
27
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas list --j\" COMP_CWORD=3 node dist/index.js)",
28
+ "Bash(rm -rf /tmp/zinkee-install)",
29
+ "Bash(mkdir -p /tmp/zinkee-install)",
30
+ "Bash(HOME=/tmp/zinkee-install SHELL=/bin/zsh node dist/index.js --install-completion)",
31
+ "Read(//tmp/**)",
32
+ "Read(//tmp/zinkee-install/**)",
33
+ "Bash(rm -rf /tmp/zinkee-install-bash)",
34
+ "Bash(mkdir -p /tmp/zinkee-install-bash)",
35
+ "Bash(HOME=/tmp/zinkee-install-bash SHELL=/bin/bash node dist/index.js --install-completion)",
36
+ "Bash(rm -rf /tmp/zinkee-install-fish)",
37
+ "Bash(mkdir -p /tmp/zinkee-install-fish)",
38
+ "Bash(_ZINKEE_COMPLETE=zsh_complete node dist/index.js)",
39
+ "Bash(echo \"exit: $?\")",
40
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas\" COMP_CWORD=abc node dist/index.js)",
41
+ "Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee config show --foo --bar baz quux ?\" COMP_CWORD=10 node dist/index.js)",
42
+ "Bash(git -C /home/guillermo/dev/cli status)",
43
+ "Bash(git -C /home/guillermo/dev/cli branch --show-current)",
44
+ "Bash(git -C /home/guillermo/dev/cli diff --stat)"
45
+ ]
46
+ }
47
+ }
package/README.md CHANGED
@@ -480,17 +480,25 @@ zinkee displays layout update <display-uuid> --template freeform --freeform-layo
480
480
  }'
481
481
  ```
482
482
 
483
- ### KPI with filters
483
+ ### Widget filters
484
484
 
485
- The `--filter` flag doesn't work on KPI widgets. Pass filters in `--raw`:
485
+ Pass `--filter "<field>:<comparator>:<value>"` (repeatable) and the CLI turns it into the structured `query.filters[]` payload the API expects. Field accepts UUID or slug. Supported comparators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `in` (comma-separated values), `is_empty`, `is_not_empty`.
486
486
 
487
487
  ```bash
488
- --raw '{"query":{"filters":[{"fieldId":"<field-uuid>","operator":"EQUALS","value":"2"}]}}'
488
+ zinkee --json displays widgets update <display-uuid> <widget-uuid> \
489
+ --filter "status:eq:2" \
490
+ --filter "amount:gte:500"
491
+ ```
492
+
493
+ For comparators the parser does not cover yet (e.g. `NOT_CONTAINS`, `RANGE`) keep using `--raw`:
494
+
495
+ ```bash
496
+ --raw '{"query":{"filters":[{"origin":{"fieldId":"<field>"},"comparisonOperator":"RANGE","values":["2025-01-01","2025-12-31"]}]}}'
489
497
  ```
490
498
 
491
499
  ### Slug support
492
500
 
493
- Slugs are resolved to UUIDs in: `--field`, `--source-schema`, `--source-field`, `--x-axis-field`, `kpi.fieldCalc`, `chart.xAxisFieldId`, `chart.yAxisSeries[].fieldCalc`, `bindings.variableMap` values.
501
+ Slugs are resolved to UUIDs in: `--field`, `--source-schema`, `--source-field`, `--x-axis-field`, `--filter` field reference, `kpi.fieldCalc`, `chart.xAxisFieldId`, `chart.yAxisSeries[].fieldCalc`, `bindings.variableMap` values.
494
502
 
495
503
  Display UUIDs are required for `displays widgets create/update` and `displays variables create`.
496
504
 
package/dist/index.js CHANGED
@@ -987,6 +987,21 @@ function parseErrorBody(text, contentType) {
987
987
  return text;
988
988
  }
989
989
  }
990
+ function summarizeNetworkCause(cause) {
991
+ const seen = /* @__PURE__ */ new Set();
992
+ let current = cause;
993
+ while (current && typeof current === "object" && !seen.has(current)) {
994
+ seen.add(current);
995
+ const candidate = current;
996
+ const message = typeof candidate.message === "string" ? candidate.message.trim() : "";
997
+ const code = typeof candidate.code === "string" ? candidate.code : void 0;
998
+ if (message && message !== "fetch failed") {
999
+ return code && !message.includes(code) ? `${message} (${code})` : message;
1000
+ }
1001
+ current = candidate.cause;
1002
+ }
1003
+ return void 0;
1004
+ }
990
1005
  var ApiClient = class {
991
1006
  constructor(options) {
992
1007
  this.options = options;
@@ -1061,7 +1076,9 @@ var ApiClient = class {
1061
1076
  cause
1062
1077
  });
1063
1078
  }
1064
- throw new ApiClientError("network_error", `Request failed for ${method} ${url}.`, {
1079
+ const causeSummary = summarizeNetworkCause(cause);
1080
+ const message = causeSummary ? `Request failed for ${method} ${url}: ${causeSummary}.` : `Request failed for ${method} ${url}.`;
1081
+ throw new ApiClientError("network_error", message, {
1065
1082
  url,
1066
1083
  method,
1067
1084
  cause
@@ -1139,7 +1156,11 @@ var cliExitCodeByErrorCode = {
1139
1156
  unknown_error: 1,
1140
1157
  invalid_cli_usage: 2,
1141
1158
  configuration_error: 2,
1142
- command_not_allowed_in_read_only_mode: 7
1159
+ command_not_allowed_in_read_only_mode: 7,
1160
+ network_error: 1,
1161
+ http_error: 1,
1162
+ timeout: 1,
1163
+ invalid_json: 1
1143
1164
  };
1144
1165
  var errorCodeByName = {
1145
1166
  ProfileResolutionError: "configuration_error"
@@ -1216,7 +1237,7 @@ function toCliError(error) {
1216
1237
  }
1217
1238
  if (error instanceof ApiClientError) {
1218
1239
  const apiError = extractApiErrorDetails(error);
1219
- return new CliError("unknown_error", apiError.message, apiError.details);
1240
+ return new CliError(error.code, apiError.message, apiError.details);
1220
1241
  }
1221
1242
  if (error instanceof Error) {
1222
1243
  return new CliError(errorCodeByName[error.name] ?? "unknown_error", error.message);
@@ -3492,29 +3513,38 @@ var specificExamples = {
3492
3513
  }`
3493
3514
  },
3494
3515
  {
3495
- description: "Add query filters that match the full comparisonOperator contract (requires --raw because --filter only accepts free-form expressions).",
3496
- 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"]}]}}'`,
3516
+ description: "Add query filters with --filter. The CLI parses the expression and emits the structured comparisonOperator payload the API expects.",
3517
+ command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --filter "status:eq:2" --filter "amount:gte:500"`,
3497
3518
  raw: `{
3498
3519
  "query": {
3499
3520
  "filters": [
3500
3521
  {
3501
- "origin": {
3502
- "schemaId": "contacts",
3503
- "fieldId": "status"
3504
- },
3522
+ "origin": { "fieldId": "status" },
3505
3523
  "comparisonOperator": "EQUAL",
3506
3524
  "values": ["2"]
3507
3525
  },
3508
3526
  {
3509
- "origin": {
3510
- "schemaId": "contacts",
3511
- "fieldId": "amount"
3512
- },
3527
+ "origin": { "fieldId": "amount" },
3513
3528
  "comparisonOperator": "GREATER_OR_EQUAL",
3514
3529
  "values": ["500"]
3515
3530
  }
3516
3531
  ]
3517
3532
  }
3533
+ }`
3534
+ },
3535
+ {
3536
+ description: "Fall back to --raw for comparators the parser does not cover yet (e.g. NOT_CONTAINS, RANGE).",
3537
+ 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"]}]}}'`,
3538
+ raw: `{
3539
+ "query": {
3540
+ "filters": [
3541
+ {
3542
+ "origin": { "fieldId": "due-date" },
3543
+ "comparisonOperator": "RANGE",
3544
+ "values": ["2025-01-01", "2025-12-31"]
3545
+ }
3546
+ ]
3547
+ }
3518
3548
  }`
3519
3549
  },
3520
3550
  {
@@ -5854,19 +5884,15 @@ function resolveProfile(config, overrides = {}) {
5854
5884
  }
5855
5885
  throw new ProfileResolutionError("No profile could be resolved from config.");
5856
5886
  }
5857
- function validateBaseUrl(value) {
5887
+ function validateBaseUrl(value, source = "Base URL") {
5858
5888
  let parsed;
5859
5889
  try {
5860
5890
  parsed = new URL(value);
5861
5891
  } catch {
5862
- throw new ProfileResolutionError(
5863
- `Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
5864
- );
5892
+ throw new ProfileResolutionError(`${source} must be an absolute http(s) URL.`);
5865
5893
  }
5866
5894
  if (!["http:", "https:"].includes(parsed.protocol) || !parsed.host) {
5867
- throw new ProfileResolutionError(
5868
- `Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
5869
- );
5895
+ throw new ProfileResolutionError(`${source} must be an absolute http(s) URL.`);
5870
5896
  }
5871
5897
  return value.replace(/\/+$/, "");
5872
5898
  }
@@ -5878,7 +5904,7 @@ function readEnvBaseUrl(env = process.env) {
5878
5904
  if (!baseUrl) {
5879
5905
  return void 0;
5880
5906
  }
5881
- return validateBaseUrl(baseUrl);
5907
+ return validateBaseUrl(baseUrl, `Environment variable ${ENV_BASE_URL}`);
5882
5908
  }
5883
5909
  function readEnvProfile(env = process.env) {
5884
5910
  return toNonEmptyString(env[ENV_PROFILE]);
@@ -6233,9 +6259,10 @@ function resolveRuntimeContext(config, overrides = {}) {
6233
6259
  const envProfile = readEnvProfile();
6234
6260
  const envApiKey = readEnvApiKey();
6235
6261
  const envBaseUrl = readEnvBaseUrl();
6262
+ const flagBaseUrl = overrides.baseUrl !== void 0 ? validateBaseUrl(overrides.baseUrl, '"--base-url"') : void 0;
6236
6263
  const profileOverride = overrides.profile ?? envProfile;
6237
6264
  const apiKeyOverride = overrides.apiKey ?? envApiKey;
6238
- const baseUrlOverride = overrides.baseUrl ?? envBaseUrl;
6265
+ const baseUrlOverride = flagBaseUrl ?? envBaseUrl;
6239
6266
  if (apiKeyOverride && !baseUrlOverride) {
6240
6267
  throw new ProfileResolutionError(
6241
6268
  `Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`
@@ -7868,6 +7895,86 @@ var DisplaysApi = class {
7868
7895
  }
7869
7896
  };
7870
7897
 
7898
+ // src/parsers/expressions.ts
7899
+ var supportedComparators = /* @__PURE__ */ new Set([
7900
+ "eq",
7901
+ "neq",
7902
+ "gt",
7903
+ "gte",
7904
+ "lt",
7905
+ "lte",
7906
+ "contains",
7907
+ "in",
7908
+ "is_empty",
7909
+ "is_not_empty"
7910
+ ]);
7911
+ var noValueComparators = /* @__PURE__ */ new Set(["is_empty", "is_not_empty"]);
7912
+ function parseColonExpression(input) {
7913
+ const parts = input.split(":");
7914
+ if (parts.length < 2) {
7915
+ throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
7916
+ }
7917
+ const field = parts[0]?.trim();
7918
+ const comparator = parts[1]?.trim();
7919
+ if (!field) {
7920
+ throw new Error(`Expected a field before ":", got "${input}".`);
7921
+ }
7922
+ if (!comparator) {
7923
+ throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
7924
+ }
7925
+ return [field, comparator, parts.slice(2).join(":").trim()];
7926
+ }
7927
+ function parseSpaceExpression(input) {
7928
+ const parts = input.trim().split(/\s+/);
7929
+ if (parts.length < 2) {
7930
+ throw new Error(`Expected a field comparator expression, got "${input}".`);
7931
+ }
7932
+ const field = parts[0];
7933
+ const comparator = parts[1];
7934
+ if (!field) {
7935
+ throw new Error(`Expected a field comparator expression, got "${input}".`);
7936
+ }
7937
+ if (!comparator) {
7938
+ throw new Error(`Expected a comparator after "${field}", got "${input}".`);
7939
+ }
7940
+ return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
7941
+ }
7942
+ function normalizeValue(comparator, rawValue) {
7943
+ if (noValueComparators.has(comparator)) {
7944
+ if (rawValue.length > 0) {
7945
+ throw new Error(`Comparator "${comparator}" does not accept a value.`);
7946
+ }
7947
+ return null;
7948
+ }
7949
+ if (comparator === "in") {
7950
+ const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
7951
+ if (values.length === 0) {
7952
+ throw new Error(`Comparator "${comparator}" requires at least one value.`);
7953
+ }
7954
+ return values;
7955
+ }
7956
+ if (rawValue.length === 0) {
7957
+ throw new Error(`Comparator "${comparator}" requires a value.`);
7958
+ }
7959
+ return rawValue;
7960
+ }
7961
+ function parseExpression(input) {
7962
+ const trimmed = input.trim();
7963
+ const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ") ? parseColonExpression(trimmed) : parseSpaceExpression(trimmed);
7964
+ if (!supportedComparators.has(comparatorCandidate)) {
7965
+ throw new Error(
7966
+ `Unsupported comparator "${comparatorCandidate}" in expression "${input}".`
7967
+ );
7968
+ }
7969
+ const comparator = comparatorCandidate;
7970
+ return {
7971
+ field,
7972
+ comparator,
7973
+ value: normalizeValue(comparator, rawValue),
7974
+ raw: trimmed
7975
+ };
7976
+ }
7977
+
7871
7978
  // src/commands/displays.ts
7872
7979
  function getCommandIo4(stdout) {
7873
7980
  return stdout ?? process.stdout;
@@ -7932,6 +8039,37 @@ function parseUuidOrSlugSelector3(input) {
7932
8039
  throw new InvalidArgumentError3(message);
7933
8040
  }
7934
8041
  }
8042
+ var COMPARATOR_TO_DISPLAY_OPERATOR = {
8043
+ eq: "EQUAL",
8044
+ neq: "NOT_EQUAL",
8045
+ gt: "GREATER",
8046
+ gte: "GREATER_OR_EQUAL",
8047
+ lt: "LESS",
8048
+ lte: "LESS_OR_EQUAL",
8049
+ contains: "CONTAINS",
8050
+ in: "ANY_OF",
8051
+ is_empty: "IS_EMPTY",
8052
+ is_not_empty: "IS_NOT_EMPTY"
8053
+ };
8054
+ function toDisplayFilter(parsed) {
8055
+ const values = parsed.value === null ? [] : Array.isArray(parsed.value) ? [...parsed.value] : [parsed.value];
8056
+ return {
8057
+ origin: { fieldId: parsed.field },
8058
+ comparisonOperator: COMPARATOR_TO_DISPLAY_OPERATOR[parsed.comparator],
8059
+ values
8060
+ };
8061
+ }
8062
+ function parseFilterExpression(input) {
8063
+ try {
8064
+ return parseExpression(input);
8065
+ } catch (error) {
8066
+ const message = error instanceof Error ? error.message : "Invalid filter expression.";
8067
+ throw new InvalidArgumentError3(message);
8068
+ }
8069
+ }
8070
+ function collectFilters(value, previous = []) {
8071
+ return [...previous, parseFilterExpression(value)];
8072
+ }
7935
8073
  function readRawObject2(raw, rawFile) {
7936
8074
  if (raw && rawFile) {
7937
8075
  throw new CliError("invalid_cli_usage", 'Use either "--raw" or "--raw-file", not both.');
@@ -7998,7 +8136,7 @@ function buildVariableBody(options) {
7998
8136
  };
7999
8137
  }
8000
8138
  if (options.filter && options.filter.length > 0) {
8001
- body.filters = options.filter.map((entry) => ({ raw: entry }));
8139
+ body.filters = options.filter.map(toDisplayFilter);
8002
8140
  }
8003
8141
  if (options.dateMode || options.dateDynamicOption || options.dateSpecific || options.dateRangeStart || options.dateRangeEnd) {
8004
8142
  body.date = {
@@ -8047,15 +8185,19 @@ function buildWidgetBody(options) {
8047
8185
  layout
8048
8186
  };
8049
8187
  }
8050
- if (options.field?.length || options.sort?.length || options.filter?.length) {
8188
+ if (options.field && options.field.length > 0) {
8051
8189
  body.view = {
8052
8190
  ...typeof body.view === "object" && body.view ? body.view : {},
8053
8191
  fields: options.field
8054
8192
  };
8193
+ }
8194
+ const queryMerge = {};
8195
+ if (options.sort && options.sort.length > 0) queryMerge.sorts = options.sort;
8196
+ if (options.filter && options.filter.length > 0) queryMerge.filters = options.filter.map(toDisplayFilter);
8197
+ if (Object.keys(queryMerge).length > 0) {
8055
8198
  body.query = {
8056
8199
  ...typeof body.query === "object" && body.query ? body.query : {},
8057
- sorts: options.sort,
8058
- filters: options.filter?.map((entry) => ({ raw: entry }))
8200
+ ...queryMerge
8059
8201
  };
8060
8202
  }
8061
8203
  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) {
@@ -8270,7 +8412,7 @@ function registerDisplaysCommands(program, options = {}) {
8270
8412
  }
8271
8413
  printTable([match], void 0, stdout);
8272
8414
  });
8273
- 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) {
8415
+ 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>").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) {
8274
8416
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8275
8417
  ensureWritable3("displays variables create", readOnly, "write");
8276
8418
  const data = await api.createVariable(display, buildVariableBody(commandOptions));
@@ -8280,7 +8422,7 @@ function registerDisplaysCommands(program, options = {}) {
8280
8422
  }
8281
8423
  writeLine4(stdout, `Created variable on display "${display.value}".`);
8282
8424
  });
8283
- 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) {
8425
+ 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>").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) {
8284
8426
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8285
8427
  ensureWritable3("displays variables update", readOnly, "write");
8286
8428
  const data = await api.updateVariable(display, variableId, buildVariableBody(commandOptions));
@@ -8320,7 +8462,7 @@ function registerDisplaysCommands(program, options = {}) {
8320
8462
  }
8321
8463
  printTable([data], void 0, stdout);
8322
8464
  });
8323
- 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) {
8465
+ 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>").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) {
8324
8466
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8325
8467
  ensureWritable3("displays widgets create", readOnly, "write");
8326
8468
  const data = await api.createWidget(display, buildWidgetBody(commandOptions));
@@ -8330,7 +8472,7 @@ function registerDisplaysCommands(program, options = {}) {
8330
8472
  }
8331
8473
  writeLine4(stdout, `Created widget on display "${display.value}".`);
8332
8474
  });
8333
- 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) {
8475
+ 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>").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) {
8334
8476
  const { api, json, readOnly } = createDisplaysApi(this, filePath);
8335
8477
  ensureWritable3("displays widgets update", readOnly, "write");
8336
8478
  const data = await api.updateWidget(display, widgetId, buildWidgetBody(commandOptions));
@@ -9049,6 +9191,19 @@ function toDisplayProfile(profileName, profile, defaultProfile) {
9049
9191
  function loadConfigForAdd(filePath) {
9050
9192
  return readConfigIfExists(filePath) ?? { profiles: {} };
9051
9193
  }
9194
+ function normalizeProfileBaseUrl(baseUrl) {
9195
+ if (baseUrl === void 0) {
9196
+ return void 0;
9197
+ }
9198
+ try {
9199
+ return validateBaseUrl(baseUrl, '"--base-url"');
9200
+ } catch (error) {
9201
+ if (error instanceof ProfileResolutionError) {
9202
+ throw new CliError("configuration_error", error.message);
9203
+ }
9204
+ throw error;
9205
+ }
9206
+ }
9052
9207
  function emitProfilesTable(stdout, data) {
9053
9208
  printTable(
9054
9209
  data.map((profile) => ({
@@ -9109,9 +9264,10 @@ function registerProfilesCommands(program, options = {}) {
9109
9264
  `Profile "${profileName}" already exists in config.`
9110
9265
  );
9111
9266
  }
9267
+ const baseUrl = normalizeProfileBaseUrl(commandOptions.baseUrl);
9112
9268
  let nextConfig = setProfileConfig(config, profileName, {
9113
9269
  apiKey: commandOptions.apiKey,
9114
- baseUrl: commandOptions.baseUrl
9270
+ baseUrl
9115
9271
  });
9116
9272
  if (!nextConfig.defaultProfile) {
9117
9273
  nextConfig = setDefaultProfile(nextConfig, profileName);
@@ -9137,9 +9293,10 @@ function registerProfilesCommands(program, options = {}) {
9137
9293
  }
9138
9294
  const config = readRequiredConfig(filePath);
9139
9295
  getRequiredProfile(config, profileName);
9296
+ const baseUrl = normalizeProfileBaseUrl(commandOptions.baseUrl);
9140
9297
  const nextConfig = setProfileConfig(config, profileName, {
9141
9298
  apiKey: commandOptions.apiKey,
9142
- baseUrl: commandOptions.baseUrl
9299
+ baseUrl
9143
9300
  });
9144
9301
  writeConfig(nextConfig, filePath);
9145
9302
  const data = toDisplayProfile(
@@ -9336,86 +9493,6 @@ var SchemasApi = class {
9336
9493
  }
9337
9494
  };
9338
9495
 
9339
- // src/parsers/expressions.ts
9340
- var supportedComparators = /* @__PURE__ */ new Set([
9341
- "eq",
9342
- "neq",
9343
- "gt",
9344
- "gte",
9345
- "lt",
9346
- "lte",
9347
- "contains",
9348
- "in",
9349
- "is_empty",
9350
- "is_not_empty"
9351
- ]);
9352
- var noValueComparators = /* @__PURE__ */ new Set(["is_empty", "is_not_empty"]);
9353
- function parseColonExpression(input) {
9354
- const parts = input.split(":");
9355
- if (parts.length < 2) {
9356
- throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
9357
- }
9358
- const field = parts[0]?.trim();
9359
- const comparator = parts[1]?.trim();
9360
- if (!field) {
9361
- throw new Error(`Expected a field before ":", got "${input}".`);
9362
- }
9363
- if (!comparator) {
9364
- throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
9365
- }
9366
- return [field, comparator, parts.slice(2).join(":").trim()];
9367
- }
9368
- function parseSpaceExpression(input) {
9369
- const parts = input.trim().split(/\s+/);
9370
- if (parts.length < 2) {
9371
- throw new Error(`Expected a field comparator expression, got "${input}".`);
9372
- }
9373
- const field = parts[0];
9374
- const comparator = parts[1];
9375
- if (!field) {
9376
- throw new Error(`Expected a field comparator expression, got "${input}".`);
9377
- }
9378
- if (!comparator) {
9379
- throw new Error(`Expected a comparator after "${field}", got "${input}".`);
9380
- }
9381
- return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
9382
- }
9383
- function normalizeValue(comparator, rawValue) {
9384
- if (noValueComparators.has(comparator)) {
9385
- if (rawValue.length > 0) {
9386
- throw new Error(`Comparator "${comparator}" does not accept a value.`);
9387
- }
9388
- return null;
9389
- }
9390
- if (comparator === "in") {
9391
- const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
9392
- if (values.length === 0) {
9393
- throw new Error(`Comparator "${comparator}" requires at least one value.`);
9394
- }
9395
- return values;
9396
- }
9397
- if (rawValue.length === 0) {
9398
- throw new Error(`Comparator "${comparator}" requires a value.`);
9399
- }
9400
- return rawValue;
9401
- }
9402
- function parseExpression(input) {
9403
- const trimmed = input.trim();
9404
- const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ") ? parseColonExpression(trimmed) : parseSpaceExpression(trimmed);
9405
- if (!supportedComparators.has(comparatorCandidate)) {
9406
- throw new Error(
9407
- `Unsupported comparator "${comparatorCandidate}" in expression "${input}".`
9408
- );
9409
- }
9410
- const comparator = comparatorCandidate;
9411
- return {
9412
- field,
9413
- comparator,
9414
- value: normalizeValue(comparator, rawValue),
9415
- raw: trimmed
9416
- };
9417
- }
9418
-
9419
9496
  // src/commands/records.ts
9420
9497
  function getCommandIo9(stdout) {
9421
9498
  return stdout ?? process.stdout;