zinkee 0.1.22 → 0.1.24
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/.claude/settings.local.json +2 -1
- package/AGENTS.md +1 -0
- package/README.md +54 -0
- package/dist/index.js +1045 -328
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/document-templates.ts +110 -0
- package/src/command-registry.ts +56 -0
- package/src/commands/automations.test.ts +130 -0
- package/src/commands/automations.ts +50 -2
- package/src/commands/document-templates.test.ts +569 -0
- package/src/commands/document-templates.ts +563 -0
- package/src/program.ts +2 -0
- package/src/utils/examples.test.ts +46 -0
- package/src/utils/examples.ts +252 -0
package/package.json
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ApiClient } from "../client.js";
|
|
2
|
+
import type { NormalizedUuidOrSlug } from "../parsers/selectors.js";
|
|
3
|
+
|
|
4
|
+
export interface ApiDocumentTemplateAsset {
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
id?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
link?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ApiDocumentTemplate {
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
id?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
assets?: ApiDocumentTemplateAsset[];
|
|
16
|
+
createdAt?: string;
|
|
17
|
+
updatedAt?: string;
|
|
18
|
+
createdBy?: string;
|
|
19
|
+
updatedBy?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CreateDocumentTemplateInput {
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
name: string;
|
|
25
|
+
assets?: ApiDocumentTemplateAsset[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface UpdateDocumentTemplateInput {
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
name?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function jsonOptions(body: unknown, method: string): RequestInit {
|
|
34
|
+
return {
|
|
35
|
+
method,
|
|
36
|
+
headers: {
|
|
37
|
+
"content-type": "application/json",
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify(body),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function selectorValue(selector: NormalizedUuidOrSlug): string {
|
|
44
|
+
return encodeURIComponent(selector.value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function segment(value: string): string {
|
|
48
|
+
return encodeURIComponent(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class DocumentTemplatesApi {
|
|
52
|
+
constructor(private readonly client: ApiClient) {}
|
|
53
|
+
|
|
54
|
+
listTemplates(): Promise<ApiDocumentTemplate[]> {
|
|
55
|
+
return this.client.getJson<ApiDocumentTemplate[]>("/api/v2/document-templates");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
createTemplate(input: CreateDocumentTemplateInput): Promise<ApiDocumentTemplate> {
|
|
59
|
+
return this.client.requestJson<ApiDocumentTemplate>(
|
|
60
|
+
"/api/v2/document-templates",
|
|
61
|
+
jsonOptions(input, "POST"),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
updateTemplate(
|
|
66
|
+
template: NormalizedUuidOrSlug,
|
|
67
|
+
input: UpdateDocumentTemplateInput,
|
|
68
|
+
): Promise<ApiDocumentTemplate> {
|
|
69
|
+
return this.client.requestJson<ApiDocumentTemplate>(
|
|
70
|
+
`/api/v2/document-templates/${selectorValue(template)}`,
|
|
71
|
+
jsonOptions(input, "PATCH"),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
deleteTemplate(template: NormalizedUuidOrSlug): Promise<Record<string, unknown>> {
|
|
76
|
+
return this.client.requestJson<Record<string, unknown>>(
|
|
77
|
+
`/api/v2/document-templates/${selectorValue(template)}`,
|
|
78
|
+
{ method: "DELETE" },
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
addAsset(
|
|
83
|
+
template: NormalizedUuidOrSlug,
|
|
84
|
+
input: FormData,
|
|
85
|
+
): Promise<ApiDocumentTemplate> {
|
|
86
|
+
return this.client.requestJson<ApiDocumentTemplate>(
|
|
87
|
+
`/api/v2/document-templates/${selectorValue(template)}/assets`,
|
|
88
|
+
{ method: "POST", body: input },
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
deleteAsset(
|
|
93
|
+
template: NormalizedUuidOrSlug,
|
|
94
|
+
assetId: string,
|
|
95
|
+
): Promise<Record<string, unknown>> {
|
|
96
|
+
return this.client.requestJson<Record<string, unknown>>(
|
|
97
|
+
`/api/v2/document-templates/${selectorValue(template)}/assets/${segment(assetId)}`,
|
|
98
|
+
{ method: "DELETE" },
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
downloadAsset(
|
|
103
|
+
template: NormalizedUuidOrSlug,
|
|
104
|
+
assetId: string,
|
|
105
|
+
): Promise<ArrayBuffer> {
|
|
106
|
+
return this.client.requestBinary(
|
|
107
|
+
`/api/v2/document-templates/${selectorValue(template)}/assets/${segment(assetId)}/download`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/command-registry.ts
CHANGED
|
@@ -441,6 +441,62 @@ export const commandRegistry = {
|
|
|
441
441
|
supportsRaw: false,
|
|
442
442
|
output: writeOutput,
|
|
443
443
|
},
|
|
444
|
+
"document-templates list": {
|
|
445
|
+
command: "document-templates list",
|
|
446
|
+
accessLevel: "read",
|
|
447
|
+
supportsExample: true,
|
|
448
|
+
supportsRaw: false,
|
|
449
|
+
output: readOutput,
|
|
450
|
+
},
|
|
451
|
+
"document-templates get": {
|
|
452
|
+
command: "document-templates get",
|
|
453
|
+
accessLevel: "read",
|
|
454
|
+
supportsExample: true,
|
|
455
|
+
supportsRaw: false,
|
|
456
|
+
output: readOutput,
|
|
457
|
+
},
|
|
458
|
+
"document-templates create": {
|
|
459
|
+
command: "document-templates create",
|
|
460
|
+
accessLevel: "write",
|
|
461
|
+
supportsExample: true,
|
|
462
|
+
supportsRaw: true,
|
|
463
|
+
output: writeOutput,
|
|
464
|
+
},
|
|
465
|
+
"document-templates update": {
|
|
466
|
+
command: "document-templates update",
|
|
467
|
+
accessLevel: "write",
|
|
468
|
+
supportsExample: true,
|
|
469
|
+
supportsRaw: false,
|
|
470
|
+
output: writeOutput,
|
|
471
|
+
},
|
|
472
|
+
"document-templates delete": {
|
|
473
|
+
command: "document-templates delete",
|
|
474
|
+
accessLevel: "destructive",
|
|
475
|
+
supportsExample: true,
|
|
476
|
+
supportsRaw: false,
|
|
477
|
+
output: writeOutput,
|
|
478
|
+
},
|
|
479
|
+
"document-templates assets add": {
|
|
480
|
+
command: "document-templates assets add",
|
|
481
|
+
accessLevel: "write",
|
|
482
|
+
supportsExample: true,
|
|
483
|
+
supportsRaw: false,
|
|
484
|
+
output: writeOutput,
|
|
485
|
+
},
|
|
486
|
+
"document-templates assets delete": {
|
|
487
|
+
command: "document-templates assets delete",
|
|
488
|
+
accessLevel: "destructive",
|
|
489
|
+
supportsExample: true,
|
|
490
|
+
supportsRaw: false,
|
|
491
|
+
output: writeOutput,
|
|
492
|
+
},
|
|
493
|
+
"document-templates assets download": {
|
|
494
|
+
command: "document-templates assets download",
|
|
495
|
+
accessLevel: "read",
|
|
496
|
+
supportsExample: true,
|
|
497
|
+
supportsRaw: false,
|
|
498
|
+
output: "binary",
|
|
499
|
+
},
|
|
444
500
|
"files upload": {
|
|
445
501
|
command: "files upload",
|
|
446
502
|
accessLevel: "write",
|
|
@@ -449,6 +449,136 @@ describe("automations commands", () => {
|
|
|
449
449
|
});
|
|
450
450
|
});
|
|
451
451
|
|
|
452
|
+
it("builds send_message actions with wrapped fieldMapping (recipients/subject/body)", async () => {
|
|
453
|
+
const configPath = createApiConfig();
|
|
454
|
+
const output = createStdoutBuffer();
|
|
455
|
+
const fetchMock = createFetchMock();
|
|
456
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
457
|
+
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
458
|
+
|
|
459
|
+
await program.parseAsync([
|
|
460
|
+
"node",
|
|
461
|
+
"zinkee",
|
|
462
|
+
"--json",
|
|
463
|
+
"automations",
|
|
464
|
+
"actions",
|
|
465
|
+
"add",
|
|
466
|
+
"automation-1",
|
|
467
|
+
"--type",
|
|
468
|
+
"send_message",
|
|
469
|
+
"--name",
|
|
470
|
+
"Notify customer",
|
|
471
|
+
"--to",
|
|
472
|
+
"customer@example.com",
|
|
473
|
+
"--subject",
|
|
474
|
+
"Your order status has changed",
|
|
475
|
+
"--message",
|
|
476
|
+
"Hello, your order is now processed.",
|
|
477
|
+
]);
|
|
478
|
+
|
|
479
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
480
|
+
type: "send_message",
|
|
481
|
+
name: "Notify customer",
|
|
482
|
+
config: {
|
|
483
|
+
fieldMapping: {
|
|
484
|
+
recipients: {
|
|
485
|
+
rawValue: "customer@example.com",
|
|
486
|
+
source: null,
|
|
487
|
+
},
|
|
488
|
+
subject: {
|
|
489
|
+
rawValue: "Your order status has changed",
|
|
490
|
+
source: null,
|
|
491
|
+
},
|
|
492
|
+
body: {
|
|
493
|
+
rawValue: "Hello, your order is now processed.",
|
|
494
|
+
source: null,
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it("wraps send_message --map values in {rawValue, source} like other field mappings", async () => {
|
|
502
|
+
const configPath = createApiConfig();
|
|
503
|
+
const output = createStdoutBuffer();
|
|
504
|
+
const fetchMock = createFetchMock();
|
|
505
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
506
|
+
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
507
|
+
|
|
508
|
+
await program.parseAsync([
|
|
509
|
+
"node",
|
|
510
|
+
"zinkee",
|
|
511
|
+
"--json",
|
|
512
|
+
"automations",
|
|
513
|
+
"actions",
|
|
514
|
+
"add",
|
|
515
|
+
"automation-1",
|
|
516
|
+
"--type",
|
|
517
|
+
"send_message",
|
|
518
|
+
"--name",
|
|
519
|
+
"Notify customer",
|
|
520
|
+
"--to",
|
|
521
|
+
"customer@example.com",
|
|
522
|
+
"--map-json",
|
|
523
|
+
'metadata={"orderId":"ORD-1"}',
|
|
524
|
+
]);
|
|
525
|
+
|
|
526
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
527
|
+
type: "send_message",
|
|
528
|
+
name: "Notify customer",
|
|
529
|
+
config: {
|
|
530
|
+
fieldMapping: {
|
|
531
|
+
recipients: {
|
|
532
|
+
rawValue: "customer@example.com",
|
|
533
|
+
source: null,
|
|
534
|
+
},
|
|
535
|
+
metadata: {
|
|
536
|
+
rawValue: { orderId: "ORD-1" },
|
|
537
|
+
source: null,
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it("builds generate_document actions with template, schema, and file-field ids", async () => {
|
|
545
|
+
const configPath = createApiConfig();
|
|
546
|
+
const output = createStdoutBuffer();
|
|
547
|
+
const fetchMock = createFetchMock();
|
|
548
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
549
|
+
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
550
|
+
|
|
551
|
+
await program.parseAsync([
|
|
552
|
+
"node",
|
|
553
|
+
"zinkee",
|
|
554
|
+
"--json",
|
|
555
|
+
"automations",
|
|
556
|
+
"actions",
|
|
557
|
+
"add",
|
|
558
|
+
"automation-1",
|
|
559
|
+
"--type",
|
|
560
|
+
"generate_document",
|
|
561
|
+
"--name",
|
|
562
|
+
"Generate invoice PDF",
|
|
563
|
+
"--document-template",
|
|
564
|
+
"6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
565
|
+
"--target-schema",
|
|
566
|
+
"68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
567
|
+
"--target-file-field",
|
|
568
|
+
"c8edbde8-a361-4e9f-a39a-2e88971f21a7",
|
|
569
|
+
]);
|
|
570
|
+
|
|
571
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
572
|
+
type: "generate_document",
|
|
573
|
+
name: "Generate invoice PDF",
|
|
574
|
+
config: {
|
|
575
|
+
documentTemplateId: "6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
576
|
+
targetSchemaId: "68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
577
|
+
targetFileFieldId: "c8edbde8-a361-4e9f-a39a-2e88971f21a7",
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
452
582
|
it("rejects source-based trigger conditions", async () => {
|
|
453
583
|
const configPath = createApiConfig();
|
|
454
584
|
const output = createStdoutBuffer();
|
|
@@ -107,6 +107,11 @@ interface ActionMutationCommandOptions {
|
|
|
107
107
|
plugin?: string;
|
|
108
108
|
arg?: string[];
|
|
109
109
|
argJson?: string[];
|
|
110
|
+
to?: string;
|
|
111
|
+
subject?: string;
|
|
112
|
+
message?: string;
|
|
113
|
+
documentTemplate?: string;
|
|
114
|
+
targetFileField?: string;
|
|
110
115
|
raw?: string;
|
|
111
116
|
rawFile?: string;
|
|
112
117
|
}
|
|
@@ -1016,6 +1021,18 @@ function buildActionInput(
|
|
|
1016
1021
|
config.targetSchemaId = options.targetSchema;
|
|
1017
1022
|
}
|
|
1018
1023
|
|
|
1024
|
+
if (options.to !== undefined) {
|
|
1025
|
+
mappings.recipients = options.to;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (options.subject !== undefined) {
|
|
1029
|
+
mappings.subject = options.subject;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (options.message !== undefined) {
|
|
1033
|
+
mappings.body = options.message;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1019
1036
|
if (!isEmptyObject(mappings)) {
|
|
1020
1037
|
config.fieldMapping = mappings;
|
|
1021
1038
|
}
|
|
@@ -1073,6 +1090,14 @@ function buildActionInput(
|
|
|
1073
1090
|
config.pluginArgs = args;
|
|
1074
1091
|
}
|
|
1075
1092
|
|
|
1093
|
+
if (options.documentTemplate !== undefined) {
|
|
1094
|
+
config.documentTemplateId = options.documentTemplate;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (options.targetFileField !== undefined) {
|
|
1098
|
+
config.targetFileFieldId = options.targetFileField;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1076
1101
|
if (!isEmptyObject(config)) {
|
|
1077
1102
|
if (isJsonObject(config.fieldMapping)) {
|
|
1078
1103
|
config.fieldMapping = normalizeAutomationFieldMapping(
|
|
@@ -1319,9 +1344,26 @@ function addTriggerPayloadOptions(command: Command): Command {
|
|
|
1319
1344
|
.option("--raw-file <path>", "path to raw trigger JSON payload");
|
|
1320
1345
|
}
|
|
1321
1346
|
|
|
1347
|
+
const ACTION_TYPES_HELP = `
|
|
1348
|
+
Action types and their flags:
|
|
1349
|
+
create_record --target-schema, --map, --map-json
|
|
1350
|
+
update_record --target-schema, --use-trigger-record, --map,
|
|
1351
|
+
--condition, --condition-json
|
|
1352
|
+
search_records --target-schema, --condition, --condition-json
|
|
1353
|
+
send_message --to, --subject, --message
|
|
1354
|
+
(--map / --map-json also accepted for extra keys)
|
|
1355
|
+
http_request --method, --url, --header, --body, --content-type,
|
|
1356
|
+
--timeout-ms, --var, --auth-mode, --connection
|
|
1357
|
+
execute_plugin --plugin, --arg, --arg-json
|
|
1358
|
+
generate_document --document-template, --target-schema, --target-file-field
|
|
1359
|
+
`;
|
|
1360
|
+
|
|
1322
1361
|
function addActionPayloadOptions(command: Command): Command {
|
|
1323
1362
|
return command
|
|
1324
|
-
.option(
|
|
1363
|
+
.option(
|
|
1364
|
+
"--type <type>",
|
|
1365
|
+
"action type (create_record|update_record|search_records|send_message|http_request|execute_plugin|generate_document)",
|
|
1366
|
+
)
|
|
1325
1367
|
.option("--name <name>", "action name")
|
|
1326
1368
|
.option("--target-schema <uuid>", "target schema id")
|
|
1327
1369
|
.option("--map <field=value>", "field mapping", collectValues, [])
|
|
@@ -1346,8 +1388,14 @@ function addActionPayloadOptions(command: Command): Command {
|
|
|
1346
1388
|
.option("--plugin <id>", "plugin id for execute_plugin")
|
|
1347
1389
|
.option("--arg <key=value>", "plugin argument", collectValues, [])
|
|
1348
1390
|
.option("--arg-json <key=json>", "plugin argument as JSON", collectValues, [])
|
|
1391
|
+
.option("--to <recipient>", "send_message recipient (maps to fieldMapping.recipients)")
|
|
1392
|
+
.option("--subject <text>", "send_message subject")
|
|
1393
|
+
.option("--message <text>", "send_message body text (maps to fieldMapping.body)")
|
|
1394
|
+
.option("--document-template <uuid>", "generate_document template id")
|
|
1395
|
+
.option("--target-file-field <uuid>", "generate_document target FileField id")
|
|
1349
1396
|
.option("--raw <json>", "raw action JSON payload")
|
|
1350
|
-
.option("--raw-file <path>", "path to raw action JSON payload")
|
|
1397
|
+
.option("--raw-file <path>", "path to raw action JSON payload")
|
|
1398
|
+
.addHelpText("after", ACTION_TYPES_HELP);
|
|
1351
1399
|
}
|
|
1352
1400
|
|
|
1353
1401
|
export function registerAutomationsCommands(
|