zinkee 0.1.39 → 0.1.41
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/.github/workflows/npm-publish.yml +77 -0
- package/.github/workflows/pr-checks.yml +36 -0
- package/AGENTS.md +4 -3
- package/dist/index.js +89 -24
- package/dist/index.js.map +1 -1
- package/docs/npm-release.md +34 -65
- package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +37 -0
- package/package.json +5 -1
- package/src/api/automations.ts +1 -0
- package/src/api/displays.ts +1 -0
- package/src/api/schemas.ts +1 -0
- package/src/commands/automations.test.ts +140 -0
- package/src/commands/automations.ts +51 -16
- package/src/commands/displays.ts +7 -2
- package/src/commands/schemas.ts +3 -2
- package/src/utils/examples.test.ts +2 -0
- package/src/utils/examples.ts +24 -4
- package/src/utils/output.test.ts +27 -1
- package/src/utils/output.ts +30 -0
- package/.claude/settings.local.json +0 -70
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
name: Publish package
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout repository
|
|
19
|
+
uses: actions/checkout@v6
|
|
20
|
+
|
|
21
|
+
- name: Set up Node.js
|
|
22
|
+
uses: actions/setup-node@v6
|
|
23
|
+
with:
|
|
24
|
+
node-version: 24
|
|
25
|
+
registry-url: https://registry.npmjs.org
|
|
26
|
+
package-manager-cache: false
|
|
27
|
+
|
|
28
|
+
- name: Check package version
|
|
29
|
+
id: version
|
|
30
|
+
shell: bash
|
|
31
|
+
run: |
|
|
32
|
+
package_name="$(node -p "require('./package.json').name")"
|
|
33
|
+
package_version="$(node -p "require('./package.json').version")"
|
|
34
|
+
|
|
35
|
+
set +e
|
|
36
|
+
lookup_output="$(npm view "${package_name}@${package_version}" version 2>&1)"
|
|
37
|
+
lookup_status=$?
|
|
38
|
+
set -e
|
|
39
|
+
|
|
40
|
+
if [ "$lookup_status" -eq 0 ]; then
|
|
41
|
+
echo "${package_name}@${package_version} is already published; skipping."
|
|
42
|
+
echo "already-published=true" >> "$GITHUB_OUTPUT"
|
|
43
|
+
elif echo "$lookup_output" | grep -q "E404"; then
|
|
44
|
+
echo "${package_name}@${package_version} is not published yet."
|
|
45
|
+
echo "already-published=false" >> "$GITHUB_OUTPUT"
|
|
46
|
+
else
|
|
47
|
+
echo "$lookup_output" >&2
|
|
48
|
+
exit "$lookup_status"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
- name: Set up trusted publishing
|
|
52
|
+
if: steps.version.outputs.already-published == 'false'
|
|
53
|
+
run: npm install --global npm@11.5.1
|
|
54
|
+
|
|
55
|
+
- name: Install dependencies
|
|
56
|
+
if: steps.version.outputs.already-published == 'false'
|
|
57
|
+
run: npm ci
|
|
58
|
+
|
|
59
|
+
- name: Run tests
|
|
60
|
+
if: steps.version.outputs.already-published == 'false'
|
|
61
|
+
run: npm test
|
|
62
|
+
|
|
63
|
+
- name: Run typecheck
|
|
64
|
+
if: steps.version.outputs.already-published == 'false'
|
|
65
|
+
run: npm run typecheck
|
|
66
|
+
|
|
67
|
+
- name: Build project
|
|
68
|
+
if: steps.version.outputs.already-published == 'false'
|
|
69
|
+
run: npm run build
|
|
70
|
+
|
|
71
|
+
- name: Inspect package contents
|
|
72
|
+
if: steps.version.outputs.already-published == 'false'
|
|
73
|
+
run: npm pack --dry-run
|
|
74
|
+
|
|
75
|
+
- name: Publish package
|
|
76
|
+
if: steps.version.outputs.already-published == 'false'
|
|
77
|
+
run: npm publish --access public
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: PR Checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
validate:
|
|
13
|
+
name: Test, Typecheck, and Build
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout repository
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Node.js
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 24
|
|
24
|
+
cache: npm
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: npm ci
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: npm test
|
|
31
|
+
|
|
32
|
+
- name: Run typecheck
|
|
33
|
+
run: npm run typecheck
|
|
34
|
+
|
|
35
|
+
- name: Build project
|
|
36
|
+
run: npm run build
|
package/AGENTS.md
CHANGED
|
@@ -101,8 +101,9 @@ Prefer focused command tests and parser/client unit tests.
|
|
|
101
101
|
|
|
102
102
|
- Versioning is patch-only today (`0.1.x`). Bump the patch number unless the
|
|
103
103
|
team decides to move to a different scheme.
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
- If an agent determines that a PR requires a new published version, it must
|
|
105
|
+
bump the version in `package.json` in that same PR and keep
|
|
106
|
+
`package-lock.json` in sync. User-visible changes (new commands, fixed
|
|
107
|
+
examples, behaviour changes) always require this.
|
|
107
108
|
- Before bumping, fetch `origin/main` to make sure the version you pick isn't
|
|
108
109
|
already taken by another PR that merged ahead of yours.
|
package/dist/index.js
CHANGED
|
@@ -1224,6 +1224,17 @@ function buildJsonSuccess(data, meta = {}) {
|
|
|
1224
1224
|
function buildJsonError(error, meta = {}) {
|
|
1225
1225
|
return { error, meta };
|
|
1226
1226
|
}
|
|
1227
|
+
var FOLDER_TABLE_KEYS = ["folderId", "folderName", "folderIsRoot"];
|
|
1228
|
+
function folderColumns(folder) {
|
|
1229
|
+
if (!folder) {
|
|
1230
|
+
return { folderId: "", folderName: "", folderIsRoot: "" };
|
|
1231
|
+
}
|
|
1232
|
+
return {
|
|
1233
|
+
folderId: folder.id ?? "",
|
|
1234
|
+
folderName: folder.name ?? (folder.isRoot === true ? "(root)" : ""),
|
|
1235
|
+
folderIsRoot: folder.isRoot === void 0 ? "" : String(folder.isRoot)
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1227
1238
|
function getWriter(writer) {
|
|
1228
1239
|
return writer ?? process.stdout;
|
|
1229
1240
|
}
|
|
@@ -4963,21 +4974,36 @@ var specificExamples = {
|
|
|
4963
4974
|
"name": "Executive Revenue",
|
|
4964
4975
|
"layoutTemplate": "widget_row_tabs",
|
|
4965
4976
|
"variableCount": 3,
|
|
4966
|
-
"widgetCount": 6
|
|
4977
|
+
"widgetCount": 6,
|
|
4978
|
+
"folder": {
|
|
4979
|
+
"id": "b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
4980
|
+
"name": "\u{1F4CA} Executive",
|
|
4981
|
+
"isRoot": false
|
|
4982
|
+
}
|
|
4967
4983
|
},
|
|
4968
4984
|
{
|
|
4969
4985
|
"id": "6b7c8d9e-0f12-3456-789a-bcdef0123456",
|
|
4970
4986
|
"name": "Sales Pipeline",
|
|
4971
4987
|
"layoutTemplate": "freeform",
|
|
4972
4988
|
"variableCount": 1,
|
|
4973
|
-
"widgetCount": 3
|
|
4989
|
+
"widgetCount": 3,
|
|
4990
|
+
"folder": {
|
|
4991
|
+
"id": "a0b1c2d3-e4f5-4061-8273-8495a6b7c8d9",
|
|
4992
|
+
"name": null,
|
|
4993
|
+
"isRoot": true
|
|
4994
|
+
}
|
|
4974
4995
|
},
|
|
4975
4996
|
{
|
|
4976
4997
|
"id": "ef012345-6789-4abc-8def-0123456789ab",
|
|
4977
4998
|
"name": "Customer Detail",
|
|
4978
4999
|
"layoutTemplate": "grid",
|
|
4979
5000
|
"variableCount": 0,
|
|
4980
|
-
"widgetCount": 2
|
|
5001
|
+
"widgetCount": 2,
|
|
5002
|
+
"folder": {
|
|
5003
|
+
"id": "b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
5004
|
+
"name": "\u{1F4CA} Executive",
|
|
5005
|
+
"isRoot": false
|
|
5006
|
+
}
|
|
4981
5007
|
}
|
|
4982
5008
|
]`
|
|
4983
5009
|
}
|
|
@@ -5109,7 +5135,12 @@ var specificExamples = {
|
|
|
5109
5135
|
"targetVariableId": "aaaaaaaa-1111-4111-a111-111111111111",
|
|
5110
5136
|
"label": "Open customer"
|
|
5111
5137
|
}
|
|
5112
|
-
]
|
|
5138
|
+
],
|
|
5139
|
+
"folder": {
|
|
5140
|
+
"id": "b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
5141
|
+
"name": "\u{1F4CA} Executive",
|
|
5142
|
+
"isRoot": false
|
|
5143
|
+
}
|
|
5113
5144
|
}`
|
|
5114
5145
|
}
|
|
5115
5146
|
]),
|
|
@@ -7361,23 +7392,45 @@ function readJsonValueOption(raw, rawFile, rawLabel, rawFileLabel) {
|
|
|
7361
7392
|
function readJsonObjectOption(raw, rawFile, rawLabel, rawFileLabel) {
|
|
7362
7393
|
return readJsonValueOption(raw, rawFile, rawLabel, rawFileLabel) ?? {};
|
|
7363
7394
|
}
|
|
7395
|
+
var NO_VALUE_COMPARATORS = /* @__PURE__ */ new Set([
|
|
7396
|
+
"chg",
|
|
7397
|
+
"changes",
|
|
7398
|
+
"ie",
|
|
7399
|
+
"is_empty",
|
|
7400
|
+
"ine",
|
|
7401
|
+
"is_not_empty"
|
|
7402
|
+
]);
|
|
7403
|
+
function isNoValueComparator(comparator) {
|
|
7404
|
+
return NO_VALUE_COMPARATORS.has(comparator.toLowerCase());
|
|
7405
|
+
}
|
|
7406
|
+
function conditionFormatError(input) {
|
|
7407
|
+
return new CliError(
|
|
7408
|
+
"invalid_cli_usage",
|
|
7409
|
+
`Expected condition in the form field:comparator:value, or field:comparator for comparators without value (chg, ie, ine), received "${input}".`
|
|
7410
|
+
);
|
|
7411
|
+
}
|
|
7364
7412
|
function parseCondition(input) {
|
|
7365
7413
|
const firstSeparator = input.indexOf(":");
|
|
7366
7414
|
const secondSeparator = input.indexOf(":", firstSeparator + 1);
|
|
7367
|
-
if (firstSeparator <= 0
|
|
7368
|
-
throw
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
);
|
|
7415
|
+
if (firstSeparator <= 0) {
|
|
7416
|
+
throw conditionFormatError(input);
|
|
7417
|
+
}
|
|
7418
|
+
if (secondSeparator === -1) {
|
|
7419
|
+
const field2 = input.slice(0, firstSeparator).trim();
|
|
7420
|
+
const comparator2 = input.slice(firstSeparator + 1).trim();
|
|
7421
|
+
if (!field2 || !comparator2 || !isNoValueComparator(comparator2)) {
|
|
7422
|
+
throw conditionFormatError(input);
|
|
7423
|
+
}
|
|
7424
|
+
return { field: field2, comparator: comparator2, values: [] };
|
|
7425
|
+
}
|
|
7426
|
+
if (secondSeparator <= firstSeparator + 1) {
|
|
7427
|
+
throw conditionFormatError(input);
|
|
7372
7428
|
}
|
|
7373
7429
|
const field = input.slice(0, firstSeparator).trim();
|
|
7374
7430
|
const comparator = input.slice(firstSeparator + 1, secondSeparator).trim();
|
|
7375
7431
|
const value = input.slice(secondSeparator + 1).trim();
|
|
7376
7432
|
if (!field || !comparator || !value) {
|
|
7377
|
-
throw
|
|
7378
|
-
"invalid_cli_usage",
|
|
7379
|
-
`Expected condition in the form field:comparator:value, received "${input}".`
|
|
7380
|
-
);
|
|
7433
|
+
throw conditionFormatError(input);
|
|
7381
7434
|
}
|
|
7382
7435
|
return {
|
|
7383
7436
|
field,
|
|
@@ -7524,10 +7577,11 @@ function normalizeAutomationConditions(conditions, label, options) {
|
|
|
7524
7577
|
const field = typeof condition.field === "string" ? condition.field.trim() : "";
|
|
7525
7578
|
const comparator = typeof condition.comparator === "string" ? condition.comparator.trim() : "";
|
|
7526
7579
|
const values = condition.values;
|
|
7527
|
-
|
|
7580
|
+
const allowsEmptyValues = isNoValueComparator(comparator);
|
|
7581
|
+
if (!field || !comparator || !Array.isArray(values) || values.length === 0 && !allowsEmptyValues) {
|
|
7528
7582
|
throw new CliError(
|
|
7529
7583
|
"invalid_cli_usage",
|
|
7530
|
-
`Expected ${label} ${index + 1} to contain "field", "comparator", and a non-empty "values" array.`
|
|
7584
|
+
`Expected ${label} ${index + 1} to contain "field", "comparator", and a non-empty "values" array (comparators without value, e.g. chg/ie/ine, accept an empty array).`
|
|
7531
7585
|
);
|
|
7532
7586
|
}
|
|
7533
7587
|
return {
|
|
@@ -7612,9 +7666,9 @@ function automationRows(automations) {
|
|
|
7612
7666
|
id: automation.id ?? "",
|
|
7613
7667
|
name: automation.name ?? "",
|
|
7614
7668
|
status: automation.status ?? "",
|
|
7615
|
-
folderId: automation.folderId ?? "",
|
|
7616
7669
|
triggerType: isJsonObject2(automation.trigger) && typeof automation.trigger.type === "string" ? automation.trigger.type : "",
|
|
7617
|
-
actionCount: Array.isArray(automation.actions) ? automation.actions.length : ""
|
|
7670
|
+
actionCount: Array.isArray(automation.actions) ? automation.actions.length : "",
|
|
7671
|
+
...folderColumns(automation.folder)
|
|
7618
7672
|
}));
|
|
7619
7673
|
}
|
|
7620
7674
|
function actionRows(actions) {
|
|
@@ -8004,7 +8058,7 @@ function addTriggerPayloadOptions(command) {
|
|
|
8004
8058
|
[]
|
|
8005
8059
|
).option(
|
|
8006
8060
|
"--condition <field:comparator:value>",
|
|
8007
|
-
"trigger condition",
|
|
8061
|
+
"trigger condition (use field:comparator for comparators without value: chg, ie, ine)",
|
|
8008
8062
|
collectValues,
|
|
8009
8063
|
[]
|
|
8010
8064
|
).option(
|
|
@@ -8031,7 +8085,12 @@ function addActionPayloadOptions(command) {
|
|
|
8031
8085
|
return command.option(
|
|
8032
8086
|
"--type <type>",
|
|
8033
8087
|
"action type (create_record|update_record|search_records|send_message|http_request|execute_plugin|generate_document)"
|
|
8034
|
-
).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(
|
|
8088
|
+
).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(
|
|
8089
|
+
"--condition <field:comparator:value>",
|
|
8090
|
+
"action condition (use field:comparator for comparators without value: ie, ine)",
|
|
8091
|
+
collectValues,
|
|
8092
|
+
[]
|
|
8093
|
+
).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(
|
|
8035
8094
|
"--timeout-ms <n>",
|
|
8036
8095
|
"HTTP timeout in milliseconds",
|
|
8037
8096
|
parseNonNegativeInteger
|
|
@@ -8136,7 +8195,7 @@ function registerAutomationsCommands(program, options = {}) {
|
|
|
8136
8195
|
}
|
|
8137
8196
|
printTable(
|
|
8138
8197
|
automationRows(data),
|
|
8139
|
-
["id", "name", "status", "
|
|
8198
|
+
["id", "name", "status", "triggerType", "actionCount", ...FOLDER_TABLE_KEYS],
|
|
8140
8199
|
stdout
|
|
8141
8200
|
);
|
|
8142
8201
|
});
|
|
@@ -9247,7 +9306,8 @@ function displayRows(displays) {
|
|
|
9247
9306
|
name: display.name ?? "",
|
|
9248
9307
|
layoutTemplate: display.layoutTemplate ?? "",
|
|
9249
9308
|
variableCount: display.variableCount ?? "",
|
|
9250
|
-
widgetCount: display.widgetCount ?? ""
|
|
9309
|
+
widgetCount: display.widgetCount ?? "",
|
|
9310
|
+
...folderColumns(display.folder)
|
|
9251
9311
|
}));
|
|
9252
9312
|
}
|
|
9253
9313
|
function getDisplaySubresource(value) {
|
|
@@ -9497,7 +9557,11 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
9497
9557
|
emitJson4(stdout, "displays list", data);
|
|
9498
9558
|
return;
|
|
9499
9559
|
}
|
|
9500
|
-
printTable(
|
|
9560
|
+
printTable(
|
|
9561
|
+
displayRows(data),
|
|
9562
|
+
["id", "name", "layoutTemplate", "variableCount", "widgetCount", ...FOLDER_TABLE_KEYS],
|
|
9563
|
+
stdout
|
|
9564
|
+
);
|
|
9501
9565
|
});
|
|
9502
9566
|
displays.command("get").description("Get a display").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).action(async function action(display) {
|
|
9503
9567
|
const { api, json } = createDisplaysApi(this, filePath);
|
|
@@ -11888,9 +11952,10 @@ function emitSchemasTable(stdout, schemas) {
|
|
|
11888
11952
|
name: schema.name ?? "",
|
|
11889
11953
|
slug: schema.slug ?? "",
|
|
11890
11954
|
auditable: schema.auditable ?? "",
|
|
11891
|
-
commentable: schema.commentable ?? ""
|
|
11955
|
+
commentable: schema.commentable ?? "",
|
|
11956
|
+
...folderColumns(schema.folder)
|
|
11892
11957
|
})),
|
|
11893
|
-
["id", "name", "slug", "auditable", "commentable"],
|
|
11958
|
+
["id", "name", "slug", "auditable", "commentable", ...FOLDER_TABLE_KEYS],
|
|
11894
11959
|
stdout
|
|
11895
11960
|
);
|
|
11896
11961
|
}
|