zinkee 0.1.44 → 0.1.46
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/README.md +4 -0
- package/dist/chunk-MHEVRMDX.js +1230 -0
- package/dist/chunk-MHEVRMDX.js.map +1 -0
- package/{src/utils/examples.ts → dist/examples-67U5IRBS.js} +866 -1141
- package/dist/examples-67U5IRBS.js.map +1 -0
- package/dist/index.js +1145 -8456
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/.github/workflows/npm-publish.yml +0 -77
- package/.github/workflows/pr-checks.yml +0 -36
- package/AGENTS.md +0 -110
- package/docs/cli-contract.md +0 -41
- package/docs/npm-release.md +0 -64
- package/docs/superpowers/plans/2026-03-24-zinkee-cli-implementation.md +0 -837
- package/docs/superpowers/plans/2026-03-25-cli-backend-error-contract.md +0 -503
- package/docs/superpowers/plans/2026-03-26-display-freeform-create.md +0 -389
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-backend-blockers.md +0 -172
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-design.md +0 -1576
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-checklist.md +0 -215
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-design.md +0 -492
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-status.md +0 -307
- package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +0 -37
- package/src/api/automations.ts +0 -404
- package/src/api/comments.ts +0 -51
- package/src/api/displays.ts +0 -337
- package/src/api/document-templates.ts +0 -110
- package/src/api/files.ts +0 -50
- package/src/api/formulas.test.ts +0 -65
- package/src/api/formulas.ts +0 -67
- package/src/api/logs.ts +0 -34
- package/src/api/navigation.ts +0 -70
- package/src/api/records.ts +0 -184
- package/src/api/schemas.ts +0 -148
- package/src/api/teamspace.ts +0 -110
- package/src/cli-examples.ts +0 -130
- package/src/cli-runner.ts +0 -95
- package/src/client.test.ts +0 -189
- package/src/client.ts +0 -269
- package/src/command-registry.ts +0 -882
- package/src/commands/automations.test.ts +0 -1030
- package/src/commands/automations.ts +0 -2102
- package/src/commands/comments.test.ts +0 -214
- package/src/commands/comments.ts +0 -303
- package/src/commands/config.test.ts +0 -81
- package/src/commands/config.ts +0 -150
- package/src/commands/displays.test.ts +0 -1105
- package/src/commands/displays.ts +0 -1442
- package/src/commands/document-templates.test.ts +0 -569
- package/src/commands/document-templates.ts +0 -563
- package/src/commands/files.test.ts +0 -284
- package/src/commands/files.ts +0 -280
- package/src/commands/formulas.test.ts +0 -194
- package/src/commands/formulas.ts +0 -243
- package/src/commands/logs.test.ts +0 -123
- package/src/commands/logs.ts +0 -159
- package/src/commands/navigation.test.ts +0 -211
- package/src/commands/navigation.ts +0 -348
- package/src/commands/profiles.test.ts +0 -191
- package/src/commands/profiles.ts +0 -303
- package/src/commands/records.test.ts +0 -860
- package/src/commands/records.ts +0 -883
- package/src/commands/schemas.test.ts +0 -1252
- package/src/commands/schemas.ts +0 -890
- package/src/commands/teamspace.test.ts +0 -229
- package/src/commands/teamspace.ts +0 -546
- package/src/completion/engine.test.ts +0 -138
- package/src/completion/engine.ts +0 -168
- package/src/completion/install.test.ts +0 -179
- package/src/completion/install.ts +0 -260
- package/src/completion/runtime.ts +0 -150
- package/src/completion/scripts.ts +0 -91
- package/src/config.test.ts +0 -362
- package/src/config.ts +0 -294
- package/src/index.test.ts +0 -217
- package/src/index.ts +0 -8
- package/src/parsers/expressions.test.ts +0 -95
- package/src/parsers/expressions.ts +0 -128
- package/src/parsers/kv.test.ts +0 -35
- package/src/parsers/kv.ts +0 -49
- package/src/parsers/selectors.test.ts +0 -23
- package/src/parsers/selectors.ts +0 -29
- package/src/program.ts +0 -64
- package/src/runtime-context.ts +0 -103
- package/src/types.ts +0 -76
- package/src/utils/argv-rewrite.test.ts +0 -149
- package/src/utils/argv-rewrite.ts +0 -269
- package/src/utils/errors.test.ts +0 -85
- package/src/utils/errors.ts +0 -191
- package/src/utils/examples.test.ts +0 -374
- package/src/utils/output.test.ts +0 -65
- package/src/utils/output.ts +0 -154
- package/src/utils/schema-fields.ts +0 -1016
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -13
|
@@ -1,2102 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import type { Writable } from "node:stream";
|
|
3
|
-
|
|
4
|
-
import { Command, InvalidArgumentError } from "commander";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
AutomationsApi,
|
|
8
|
-
type ApiAutomation,
|
|
9
|
-
type ApiAutomationAction,
|
|
10
|
-
type ApiAutomationConnection,
|
|
11
|
-
type ApiAutomationFolder,
|
|
12
|
-
type ApiAutomationPlugin,
|
|
13
|
-
type CreateAutomationConnectionInput,
|
|
14
|
-
type CreateAutomationInput,
|
|
15
|
-
type MutationAutomationActionInput,
|
|
16
|
-
type SetAutomationFlowInput,
|
|
17
|
-
type SetAutomationTriggerInput,
|
|
18
|
-
type SetAutomationWebhookInput,
|
|
19
|
-
type UpdateAutomationConnectionInput,
|
|
20
|
-
type UpdateAutomationInput,
|
|
21
|
-
} from "../api/automations.js";
|
|
22
|
-
import { ApiClient } from "../client.js";
|
|
23
|
-
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
24
|
-
import { parseJsonAssignment, parseKeyValue } from "../parsers/kv.js";
|
|
25
|
-
import {
|
|
26
|
-
normalizeUuidOrSlug,
|
|
27
|
-
type NormalizedUuidOrSlug,
|
|
28
|
-
} from "../parsers/selectors.js";
|
|
29
|
-
import { resolveRuntimeContext } from "../runtime-context.js";
|
|
30
|
-
import type { JsonObject, JsonValue, RuntimeOverrides } from "../types.js";
|
|
31
|
-
import { CliError } from "../utils/errors.js";
|
|
32
|
-
import { buildJsonSuccess, folderColumns, FOLDER_TABLE_KEYS, printTable } from "../utils/output.js";
|
|
33
|
-
|
|
34
|
-
interface CommandIo {
|
|
35
|
-
write(chunk: string): unknown;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface RegisterAutomationsCommandsOptions {
|
|
39
|
-
configPath?: string;
|
|
40
|
-
stdout?: CommandIo;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface GlobalOptions extends RuntimeOverrides {}
|
|
44
|
-
|
|
45
|
-
interface FolderMutationCommandOptions {
|
|
46
|
-
name?: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface FolderMoveCommandOptions {
|
|
50
|
-
to?: NormalizedUuidOrSlug;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface AutomationListCommandOptions {
|
|
54
|
-
folder?: NormalizedUuidOrSlug;
|
|
55
|
-
status?: string;
|
|
56
|
-
triggerType?: string;
|
|
57
|
-
actionType?: string;
|
|
58
|
-
plugin?: string;
|
|
59
|
-
search?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface TriggerPayloadCommandOptions {
|
|
63
|
-
triggerType?: string;
|
|
64
|
-
cron?: string;
|
|
65
|
-
triggerSchema?: string;
|
|
66
|
-
triggerField?: string[];
|
|
67
|
-
condition?: string[];
|
|
68
|
-
conditionJson?: string[];
|
|
69
|
-
webhookEndpointId?: string;
|
|
70
|
-
raw?: string;
|
|
71
|
-
rawFile?: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
interface AutomationCreateCommandOptions extends TriggerPayloadCommandOptions {
|
|
75
|
-
name?: string;
|
|
76
|
-
description?: string;
|
|
77
|
-
folder?: NormalizedUuidOrSlug;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface AutomationUpdateCommandOptions {
|
|
81
|
-
name?: string;
|
|
82
|
-
description?: string;
|
|
83
|
-
folder?: NormalizedUuidOrSlug;
|
|
84
|
-
raw?: string;
|
|
85
|
-
rawFile?: string;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
interface ActionMutationCommandOptions {
|
|
89
|
-
type?: string;
|
|
90
|
-
name?: string;
|
|
91
|
-
targetSchema?: string;
|
|
92
|
-
map?: string[];
|
|
93
|
-
mapJson?: string[];
|
|
94
|
-
condition?: string[];
|
|
95
|
-
conditionJson?: string[];
|
|
96
|
-
useTriggerRecord?: boolean;
|
|
97
|
-
method?: string;
|
|
98
|
-
url?: string;
|
|
99
|
-
header?: string[];
|
|
100
|
-
body?: string;
|
|
101
|
-
bodyFile?: string;
|
|
102
|
-
contentType?: string;
|
|
103
|
-
timeoutMs?: number;
|
|
104
|
-
var?: string[];
|
|
105
|
-
authMode?: string;
|
|
106
|
-
connection?: string;
|
|
107
|
-
plugin?: string;
|
|
108
|
-
arg?: string[];
|
|
109
|
-
argJson?: string[];
|
|
110
|
-
to?: string;
|
|
111
|
-
subject?: string;
|
|
112
|
-
message?: string;
|
|
113
|
-
documentTemplate?: string;
|
|
114
|
-
targetFileField?: string;
|
|
115
|
-
raw?: string;
|
|
116
|
-
rawFile?: string;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
interface FlowSetCommandOptions {
|
|
120
|
-
entry?: string[];
|
|
121
|
-
transition?: string[];
|
|
122
|
-
raw?: string;
|
|
123
|
-
rawFile?: string;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
interface WebhookSetCommandOptions {
|
|
127
|
-
active?: boolean;
|
|
128
|
-
idempotencyKeyPath?: string;
|
|
129
|
-
eventTypePath?: string;
|
|
130
|
-
allowedEventType?: string[];
|
|
131
|
-
fieldMapping?: string[];
|
|
132
|
-
fieldMappingFile?: string[];
|
|
133
|
-
raw?: string;
|
|
134
|
-
rawFile?: string;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
interface ConnectionCreateCommandOptions {
|
|
138
|
-
name?: string;
|
|
139
|
-
type?: string;
|
|
140
|
-
scope?: string;
|
|
141
|
-
ownerMember?: string;
|
|
142
|
-
config?: string;
|
|
143
|
-
configFile?: string;
|
|
144
|
-
secret?: string[];
|
|
145
|
-
active?: boolean;
|
|
146
|
-
raw?: string;
|
|
147
|
-
rawFile?: string;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
interface ConnectionUpdateCommandOptions {
|
|
151
|
-
set?: string[];
|
|
152
|
-
unset?: string[];
|
|
153
|
-
raw?: string;
|
|
154
|
-
rawFile?: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
type ConditionSpec = JsonObject & {
|
|
158
|
-
field: string;
|
|
159
|
-
comparator: string;
|
|
160
|
-
values: JsonValue[];
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
type TransitionSpec = JsonObject & {
|
|
164
|
-
fromActionId: string;
|
|
165
|
-
toActionId: string;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
function getCommandIo(stdout: CommandIo | undefined): CommandIo {
|
|
169
|
-
return stdout ?? (process.stdout as Writable);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function writeLine(stdout: CommandIo, value: string): void {
|
|
173
|
-
stdout.write(`${value}\n`);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function getGlobalOptions(command: Command): GlobalOptions {
|
|
177
|
-
return command.optsWithGlobals<GlobalOptions>();
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function emitJson(
|
|
181
|
-
stdout: CommandIo,
|
|
182
|
-
commandName: string,
|
|
183
|
-
data: unknown,
|
|
184
|
-
configPath: string,
|
|
185
|
-
): void {
|
|
186
|
-
writeLine(
|
|
187
|
-
stdout,
|
|
188
|
-
JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath })),
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function toConfigurationError(filePath: string, error: unknown): never {
|
|
193
|
-
if (error instanceof CliError) {
|
|
194
|
-
throw error;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
typeof error === "object" &&
|
|
199
|
-
error !== null &&
|
|
200
|
-
"code" in error &&
|
|
201
|
-
error.code === "ENOENT"
|
|
202
|
-
) {
|
|
203
|
-
throw new CliError("configuration_error", `Config file not found at ${filePath}.`);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (error instanceof Error) {
|
|
207
|
-
throw new CliError("configuration_error", error.message);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
throw error;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function createAutomationsApi(
|
|
214
|
-
command: Command,
|
|
215
|
-
filePath: string,
|
|
216
|
-
): { api: AutomationsApi; json: boolean; readOnly: boolean } {
|
|
217
|
-
try {
|
|
218
|
-
const config = readRuntimeConfig(filePath);
|
|
219
|
-
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
220
|
-
|
|
221
|
-
if (!runtime.baseUrl) {
|
|
222
|
-
throw new CliError(
|
|
223
|
-
"configuration_error",
|
|
224
|
-
`Resolved profile "${runtime.profile}" is missing a base URL.`,
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return {
|
|
229
|
-
api: new AutomationsApi(
|
|
230
|
-
new ApiClient({
|
|
231
|
-
baseUrl: runtime.baseUrl,
|
|
232
|
-
apiKey: runtime.apiKey,
|
|
233
|
-
}),
|
|
234
|
-
),
|
|
235
|
-
json: runtime.json,
|
|
236
|
-
readOnly: runtime.readOnly,
|
|
237
|
-
};
|
|
238
|
-
} catch (error) {
|
|
239
|
-
toConfigurationError(filePath, error);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function ensureWritable(
|
|
244
|
-
commandName: string,
|
|
245
|
-
readOnly: boolean,
|
|
246
|
-
commandMode: "write" | "destructive",
|
|
247
|
-
): void {
|
|
248
|
-
if (readOnly) {
|
|
249
|
-
throw new CliError(
|
|
250
|
-
"command_not_allowed_in_read_only_mode",
|
|
251
|
-
`Command "${commandName}" is not allowed in read-only mode.`,
|
|
252
|
-
{
|
|
253
|
-
commandMode,
|
|
254
|
-
executionMode: "read-only",
|
|
255
|
-
},
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function parseUuidOrSlugSelector(input: string): NormalizedUuidOrSlug {
|
|
261
|
-
try {
|
|
262
|
-
return normalizeUuidOrSlug(input);
|
|
263
|
-
} catch (error) {
|
|
264
|
-
const message = error instanceof Error ? error.message : "Invalid selector.";
|
|
265
|
-
throw new InvalidArgumentError(message);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function collectValues(value: string, previous: string[] = []): string[] {
|
|
270
|
-
return [...previous, value];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function parseNonNegativeInteger(input: string): number {
|
|
274
|
-
if (!/^\d+$/.test(input.trim())) {
|
|
275
|
-
throw new InvalidArgumentError(`Expected a non-negative integer, received "${input}".`);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return Number(input);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function isJsonObject(value: unknown): value is JsonObject {
|
|
282
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function isEmptyObject(value: object): boolean {
|
|
286
|
-
return Object.keys(value).length === 0;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function readRawSetPayload(raw: JsonObject, label: string): JsonObject {
|
|
290
|
-
const setValue = raw.set;
|
|
291
|
-
|
|
292
|
-
if (setValue === undefined) {
|
|
293
|
-
return {};
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (!isJsonObject(setValue)) {
|
|
297
|
-
throw new CliError(
|
|
298
|
-
"invalid_cli_usage",
|
|
299
|
-
`Expected ${label} field "set" to be a JSON object.`,
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return { ...setValue };
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function readRawUnsetPayload(raw: JsonObject, label: string): string[] {
|
|
307
|
-
const unsetValue = raw.unset;
|
|
308
|
-
|
|
309
|
-
if (unsetValue === undefined) {
|
|
310
|
-
return [];
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (!Array.isArray(unsetValue) || !unsetValue.every((value) => typeof value === "string")) {
|
|
314
|
-
throw new CliError(
|
|
315
|
-
"invalid_cli_usage",
|
|
316
|
-
`Expected ${label} field "unset" to be an array of strings.`,
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return [...unsetValue];
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function splitRawPatchPayload(
|
|
324
|
-
raw: JsonObject,
|
|
325
|
-
label: string,
|
|
326
|
-
): { set: JsonObject; unset: string[] } {
|
|
327
|
-
const set: JsonObject = { ...raw };
|
|
328
|
-
delete set.set;
|
|
329
|
-
delete set.unset;
|
|
330
|
-
|
|
331
|
-
return {
|
|
332
|
-
set: {
|
|
333
|
-
...set,
|
|
334
|
-
...readRawSetPayload(raw, label),
|
|
335
|
-
},
|
|
336
|
-
unset: readRawUnsetPayload(raw, label),
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function setNestedPath(target: JsonObject, path: readonly string[], value: JsonValue): void {
|
|
341
|
-
const [head, ...tail] = path;
|
|
342
|
-
|
|
343
|
-
if (!head) {
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (tail.length === 0) {
|
|
348
|
-
target[head] = value;
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const next = isJsonObject(target[head]) ? { ...target[head] } : {};
|
|
353
|
-
target[head] = next;
|
|
354
|
-
setNestedPath(next, tail, value);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function hasSetPath(target: JsonObject, path: string): boolean {
|
|
358
|
-
if (Object.hasOwn(target, path)) {
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const segments = path.split(".").filter(Boolean);
|
|
363
|
-
|
|
364
|
-
if (segments.length <= 1) {
|
|
365
|
-
return Object.hasOwn(target, path);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
let current: unknown = target;
|
|
369
|
-
|
|
370
|
-
for (const segment of segments) {
|
|
371
|
-
if (!isJsonObject(current) || !Object.hasOwn(current, segment)) {
|
|
372
|
-
return false;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
current = current[segment];
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function parseJsonValue(input: string, sourceLabel: string): JsonValue {
|
|
382
|
-
try {
|
|
383
|
-
return JSON.parse(input) as JsonValue;
|
|
384
|
-
} catch (error) {
|
|
385
|
-
const message = error instanceof Error ? error.message : "Invalid JSON.";
|
|
386
|
-
throw new CliError(
|
|
387
|
-
"invalid_cli_usage",
|
|
388
|
-
`Failed to parse JSON from ${sourceLabel}: ${message}`,
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function parseJsonObject(input: string, sourceLabel: string): JsonObject {
|
|
394
|
-
const parsed = parseJsonValue(input, sourceLabel);
|
|
395
|
-
|
|
396
|
-
if (!isJsonObject(parsed)) {
|
|
397
|
-
throw new CliError(
|
|
398
|
-
"invalid_cli_usage",
|
|
399
|
-
`Expected ${sourceLabel} to contain a JSON object.`,
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return parsed;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function readTextFile(filePath: string, label: string): string {
|
|
407
|
-
try {
|
|
408
|
-
return readFileSync(filePath, "utf8");
|
|
409
|
-
} catch (error) {
|
|
410
|
-
const message = error instanceof Error ? error.message : "Unable to read file.";
|
|
411
|
-
throw new CliError(
|
|
412
|
-
"invalid_cli_usage",
|
|
413
|
-
`Failed to read ${label} from ${filePath}: ${message}`,
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function readJsonValueOption(
|
|
419
|
-
raw: string | undefined,
|
|
420
|
-
rawFile: string | undefined,
|
|
421
|
-
rawLabel: string,
|
|
422
|
-
rawFileLabel: string,
|
|
423
|
-
): JsonValue | undefined {
|
|
424
|
-
if (raw && rawFile) {
|
|
425
|
-
throw new CliError(
|
|
426
|
-
"invalid_cli_usage",
|
|
427
|
-
`Provide either "${rawLabel}" or "${rawFileLabel}", not both.`,
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (raw) {
|
|
432
|
-
return parseJsonValue(raw, rawLabel);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (rawFile) {
|
|
436
|
-
return parseJsonValue(readTextFile(rawFile, rawFileLabel), `${rawFileLabel} ${rawFile}`);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return undefined;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
function readJsonObjectOption(
|
|
443
|
-
raw: string | undefined,
|
|
444
|
-
rawFile: string | undefined,
|
|
445
|
-
rawLabel: string,
|
|
446
|
-
rawFileLabel: string,
|
|
447
|
-
): JsonObject {
|
|
448
|
-
return (
|
|
449
|
-
readJsonValueOption(raw, rawFile, rawLabel, rawFileLabel) ?? {}
|
|
450
|
-
) as JsonObject;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const NO_VALUE_COMPARATORS = new Set([
|
|
454
|
-
"chg",
|
|
455
|
-
"changes",
|
|
456
|
-
"ie",
|
|
457
|
-
"is_empty",
|
|
458
|
-
"ine",
|
|
459
|
-
"is_not_empty",
|
|
460
|
-
]);
|
|
461
|
-
|
|
462
|
-
function isNoValueComparator(comparator: string): boolean {
|
|
463
|
-
return NO_VALUE_COMPARATORS.has(comparator.toLowerCase());
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function conditionFormatError(input: string): CliError {
|
|
467
|
-
return new CliError(
|
|
468
|
-
"invalid_cli_usage",
|
|
469
|
-
`Expected condition in the form field:comparator:value, or field:comparator for comparators without value (chg, ie, ine), received "${input}".`,
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function parseCondition(input: string): ConditionSpec {
|
|
474
|
-
const firstSeparator = input.indexOf(":");
|
|
475
|
-
const secondSeparator = input.indexOf(":", firstSeparator + 1);
|
|
476
|
-
|
|
477
|
-
if (firstSeparator <= 0) {
|
|
478
|
-
throw conditionFormatError(input);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (secondSeparator === -1) {
|
|
482
|
-
const field = input.slice(0, firstSeparator).trim();
|
|
483
|
-
const comparator = input.slice(firstSeparator + 1).trim();
|
|
484
|
-
|
|
485
|
-
if (!field || !comparator || !isNoValueComparator(comparator)) {
|
|
486
|
-
throw conditionFormatError(input);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return { field, comparator, values: [] };
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (secondSeparator <= firstSeparator + 1) {
|
|
493
|
-
throw conditionFormatError(input);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const field = input.slice(0, firstSeparator).trim();
|
|
497
|
-
const comparator = input.slice(firstSeparator + 1, secondSeparator).trim();
|
|
498
|
-
const value = input.slice(secondSeparator + 1).trim();
|
|
499
|
-
|
|
500
|
-
if (!field || !comparator || !value) {
|
|
501
|
-
throw conditionFormatError(input);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return {
|
|
505
|
-
field,
|
|
506
|
-
comparator,
|
|
507
|
-
values: value
|
|
508
|
-
.split(",")
|
|
509
|
-
.map((entry) => entry.trim())
|
|
510
|
-
.filter(Boolean),
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function parseJsonCondition(input: string, label: string): ConditionSpec {
|
|
515
|
-
const parsed = parseJsonObject(input, label);
|
|
516
|
-
const field = typeof parsed.field === "string" ? parsed.field.trim() : "";
|
|
517
|
-
const comparator = typeof parsed.comparator === "string" ? parsed.comparator.trim() : "";
|
|
518
|
-
const values = parsed.values;
|
|
519
|
-
|
|
520
|
-
if (!field || !comparator || !Array.isArray(values)) {
|
|
521
|
-
throw new CliError(
|
|
522
|
-
"invalid_cli_usage",
|
|
523
|
-
`Expected ${label} to contain {"field","comparator","values"}.`,
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
return {
|
|
528
|
-
...parsed,
|
|
529
|
-
field,
|
|
530
|
-
comparator,
|
|
531
|
-
values: [...values],
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function parseTransition(input: string): TransitionSpec {
|
|
536
|
-
const separatorIndex = input.indexOf(":");
|
|
537
|
-
|
|
538
|
-
if (separatorIndex <= 0 || separatorIndex === input.length - 1) {
|
|
539
|
-
throw new CliError(
|
|
540
|
-
"invalid_cli_usage",
|
|
541
|
-
`Expected transition in the form from:to, received "${input}".`,
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const fromActionId = input.slice(0, separatorIndex).trim();
|
|
546
|
-
const toActionId = input.slice(separatorIndex + 1).trim();
|
|
547
|
-
|
|
548
|
-
if (!fromActionId || !toActionId) {
|
|
549
|
-
throw new CliError(
|
|
550
|
-
"invalid_cli_usage",
|
|
551
|
-
`Expected transition in the form from:to, received "${input}".`,
|
|
552
|
-
);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
return { fromActionId, toActionId };
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function parseStringAssignments(values: readonly string[] | undefined): Record<string, string> {
|
|
559
|
-
const result: Record<string, string> = {};
|
|
560
|
-
|
|
561
|
-
for (const value of values ?? []) {
|
|
562
|
-
const assignment = parseKeyValue(value);
|
|
563
|
-
result[assignment.key] = assignment.value;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
return result;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function parseJsonAssignments(values: readonly string[] | undefined): JsonObject {
|
|
570
|
-
const result: JsonObject = {};
|
|
571
|
-
|
|
572
|
-
for (const value of values ?? []) {
|
|
573
|
-
const assignment = parseJsonAssignment<JsonValue>(value);
|
|
574
|
-
result[assignment.key] = assignment.value;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
return result;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function mergeMappings(
|
|
581
|
-
stringAssignments: readonly string[] | undefined,
|
|
582
|
-
jsonAssignments: readonly string[] | undefined,
|
|
583
|
-
): JsonObject {
|
|
584
|
-
return {
|
|
585
|
-
...parseStringAssignments(stringAssignments),
|
|
586
|
-
...parseJsonAssignments(jsonAssignments),
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
const AUTOMATION_SOURCE_TYPES = new Set(["TRIGGER", "PREVIOUS_ACTION"]);
|
|
591
|
-
|
|
592
|
-
type AutomationSourceType = "TRIGGER" | "PREVIOUS_ACTION";
|
|
593
|
-
|
|
594
|
-
type AutomationValueSource = JsonObject & {
|
|
595
|
-
type: AutomationSourceType;
|
|
596
|
-
id: string;
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
type AutomationResolvedValue = JsonObject & {
|
|
600
|
-
rawValue: JsonValue | null;
|
|
601
|
-
source: AutomationValueSource | null;
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
function isAutomationValueSource(value: unknown): value is AutomationValueSource {
|
|
605
|
-
return (
|
|
606
|
-
isJsonObject(value) &&
|
|
607
|
-
typeof value.type === "string" &&
|
|
608
|
-
AUTOMATION_SOURCE_TYPES.has(value.type) &&
|
|
609
|
-
typeof value.id === "string" &&
|
|
610
|
-
value.id.trim().length > 0
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
function looksLikeAutomationResolvedValue(value: unknown): value is JsonObject {
|
|
615
|
-
if (!isJsonObject(value)) {
|
|
616
|
-
return false;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
if ("rawValue" in value) {
|
|
620
|
-
return true;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
if (!Object.hasOwn(value, "source")) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
return value.source === null || isAutomationValueSource(value.source);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
function normalizeAutomationResolvedValue(
|
|
631
|
-
value: JsonValue,
|
|
632
|
-
label: string,
|
|
633
|
-
options: { allowSource: boolean },
|
|
634
|
-
): AutomationResolvedValue {
|
|
635
|
-
if (!looksLikeAutomationResolvedValue(value)) {
|
|
636
|
-
return {
|
|
637
|
-
rawValue: value,
|
|
638
|
-
source: null,
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
const rawValue = Object.hasOwn(value, "rawValue") ? (value.rawValue as JsonValue | null) : undefined;
|
|
643
|
-
const sourceValue = Object.hasOwn(value, "source") ? value.source : undefined;
|
|
644
|
-
const hasRawValue = rawValue !== undefined && rawValue !== null;
|
|
645
|
-
const hasSource = sourceValue !== undefined && sourceValue !== null;
|
|
646
|
-
|
|
647
|
-
if (hasRawValue === hasSource) {
|
|
648
|
-
throw new CliError(
|
|
649
|
-
"invalid_cli_usage",
|
|
650
|
-
`Expected ${label} to define exactly one of "rawValue" or "source".`,
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if (hasSource) {
|
|
655
|
-
if (!options.allowSource) {
|
|
656
|
-
throw new CliError(
|
|
657
|
-
"invalid_cli_usage",
|
|
658
|
-
`Expected ${label} to use only literal "rawValue" values.`,
|
|
659
|
-
);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
if (!isAutomationValueSource(sourceValue)) {
|
|
663
|
-
throw new CliError(
|
|
664
|
-
"invalid_cli_usage",
|
|
665
|
-
`Expected ${label} "source" to contain a valid automation source reference.`,
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
return {
|
|
670
|
-
rawValue: null,
|
|
671
|
-
source: {
|
|
672
|
-
type: sourceValue.type,
|
|
673
|
-
id: sourceValue.id,
|
|
674
|
-
},
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return {
|
|
679
|
-
rawValue: rawValue ?? null,
|
|
680
|
-
source: null,
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
function normalizeAutomationFieldMapping(mapping: JsonObject, label: string): JsonObject {
|
|
685
|
-
const normalized: JsonObject = {};
|
|
686
|
-
|
|
687
|
-
for (const [field, value] of Object.entries(mapping)) {
|
|
688
|
-
normalized[field] = normalizeAutomationResolvedValue(
|
|
689
|
-
value as JsonValue,
|
|
690
|
-
`${label} "${field}"`,
|
|
691
|
-
{ allowSource: true },
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
return normalized;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
function normalizeAutomationConditions(
|
|
699
|
-
conditions: readonly unknown[],
|
|
700
|
-
label: string,
|
|
701
|
-
options: { allowSource: boolean },
|
|
702
|
-
): ConditionSpec[] {
|
|
703
|
-
return conditions.map((condition, index) => {
|
|
704
|
-
if (!isJsonObject(condition)) {
|
|
705
|
-
throw new CliError(
|
|
706
|
-
"invalid_cli_usage",
|
|
707
|
-
`Expected ${label} ${index + 1} to be a JSON object.`,
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const field = typeof condition.field === "string" ? condition.field.trim() : "";
|
|
712
|
-
const comparator = typeof condition.comparator === "string" ? condition.comparator.trim() : "";
|
|
713
|
-
const values = condition.values;
|
|
714
|
-
|
|
715
|
-
const allowsEmptyValues = isNoValueComparator(comparator);
|
|
716
|
-
if (!field || !comparator || !Array.isArray(values) || (values.length === 0 && !allowsEmptyValues)) {
|
|
717
|
-
throw new CliError(
|
|
718
|
-
"invalid_cli_usage",
|
|
719
|
-
`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).`,
|
|
720
|
-
);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
return {
|
|
724
|
-
...condition,
|
|
725
|
-
field,
|
|
726
|
-
comparator,
|
|
727
|
-
values: values.map((value, valueIndex) =>
|
|
728
|
-
normalizeAutomationResolvedValue(
|
|
729
|
-
value as JsonValue,
|
|
730
|
-
`${label} ${index + 1} value ${valueIndex + 1}`,
|
|
731
|
-
options,
|
|
732
|
-
),
|
|
733
|
-
),
|
|
734
|
-
};
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
function readConditions(
|
|
739
|
-
literalConditions: readonly string[] | undefined,
|
|
740
|
-
jsonConditions: readonly string[] | undefined,
|
|
741
|
-
label: string,
|
|
742
|
-
): ConditionSpec[] {
|
|
743
|
-
return [
|
|
744
|
-
...(literalConditions ?? []).map(parseCondition),
|
|
745
|
-
...(jsonConditions ?? []).map((value, index) =>
|
|
746
|
-
parseJsonCondition(value, `${label} ${index + 1}`),
|
|
747
|
-
),
|
|
748
|
-
];
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function resolveFolder(
|
|
752
|
-
folders: readonly ApiAutomationFolder[],
|
|
753
|
-
selector: NormalizedUuidOrSlug,
|
|
754
|
-
): ApiAutomationFolder {
|
|
755
|
-
if (selector.kind === "uuid") {
|
|
756
|
-
const match = folders.find((folder) => folder.id === selector.value);
|
|
757
|
-
|
|
758
|
-
if (match) {
|
|
759
|
-
return match;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
const matches = folders.filter((folder) => folder.name === selector.value);
|
|
764
|
-
|
|
765
|
-
if (matches.length === 1) {
|
|
766
|
-
return matches[0] as ApiAutomationFolder;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
if (matches.length > 1) {
|
|
770
|
-
throw new CliError(
|
|
771
|
-
"invalid_cli_usage",
|
|
772
|
-
`Folder "${selector.value}" is ambiguous. Found ${matches.length} matches.`,
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
throw new CliError("invalid_cli_usage", `Folder "${selector.value}" was not found.`);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
async function resolveFolderSelector(
|
|
780
|
-
api: AutomationsApi,
|
|
781
|
-
selector: NormalizedUuidOrSlug | undefined,
|
|
782
|
-
): Promise<NormalizedUuidOrSlug | undefined> {
|
|
783
|
-
if (!selector) {
|
|
784
|
-
return undefined;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
if (selector.kind === "uuid") {
|
|
788
|
-
return selector;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
const folders = await api.listFolders();
|
|
792
|
-
const resolved = resolveFolder(folders, selector);
|
|
793
|
-
|
|
794
|
-
if (typeof resolved.id !== "string" || resolved.id.length === 0) {
|
|
795
|
-
throw new CliError(
|
|
796
|
-
"invalid_cli_usage",
|
|
797
|
-
`Folder "${selector.value}" is missing an id in the API response.`,
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
return { kind: "uuid", value: resolved.id };
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function toTableRows(data: unknown): Record<string, unknown>[] {
|
|
805
|
-
if (Array.isArray(data)) {
|
|
806
|
-
return data.map((entry) => (isJsonObject(entry) ? entry : { value: entry }));
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (isJsonObject(data)) {
|
|
810
|
-
return [data];
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
return [{ value: data }];
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
function emitTable(stdout: CommandIo, data: unknown): void {
|
|
817
|
-
printTable(toTableRows(data), undefined, stdout);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
function folderRows(folders: readonly ApiAutomationFolder[]): Record<string, unknown>[] {
|
|
821
|
-
return folders.map((folder) => ({
|
|
822
|
-
id: folder.id ?? "",
|
|
823
|
-
name: folder.name ?? "",
|
|
824
|
-
automationCount: folder.automationCount ?? "",
|
|
825
|
-
}));
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
function automationRows(automations: readonly ApiAutomation[]): Record<string, unknown>[] {
|
|
829
|
-
return automations.map((automation) => ({
|
|
830
|
-
id: automation.id ?? "",
|
|
831
|
-
name: automation.name ?? "",
|
|
832
|
-
status: automation.status ?? "",
|
|
833
|
-
triggerType:
|
|
834
|
-
isJsonObject(automation.trigger) && typeof automation.trigger.type === "string"
|
|
835
|
-
? automation.trigger.type
|
|
836
|
-
: "",
|
|
837
|
-
actionCount: Array.isArray(automation.actions) ? automation.actions.length : "",
|
|
838
|
-
...folderColumns(automation.folder),
|
|
839
|
-
}));
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
function actionRows(actions: readonly ApiAutomationAction[]): Record<string, unknown>[] {
|
|
843
|
-
return actions.map((action) => ({
|
|
844
|
-
id: action.id ?? "",
|
|
845
|
-
name: action.name ?? "",
|
|
846
|
-
type: action.type ?? "",
|
|
847
|
-
}));
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
function pluginRows(plugins: readonly ApiAutomationPlugin[]): Record<string, unknown>[] {
|
|
851
|
-
return plugins.map((plugin) => ({
|
|
852
|
-
id: plugin.id ?? "",
|
|
853
|
-
name: plugin.name ?? "",
|
|
854
|
-
version: plugin.version ?? "",
|
|
855
|
-
}));
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
function connectionRows(
|
|
859
|
-
connections: readonly ApiAutomationConnection[],
|
|
860
|
-
): Record<string, unknown>[] {
|
|
861
|
-
return connections.map((connection) => ({
|
|
862
|
-
id: connection.id ?? "",
|
|
863
|
-
name: connection.name ?? "",
|
|
864
|
-
type: connection.type ?? "",
|
|
865
|
-
scope: connection.scope ?? "",
|
|
866
|
-
active: connection.active ?? "",
|
|
867
|
-
}));
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
function buildFolderMutationInput(
|
|
871
|
-
options: FolderMutationCommandOptions,
|
|
872
|
-
requireName: boolean,
|
|
873
|
-
): Record<string, unknown> {
|
|
874
|
-
const input: Record<string, unknown> = {};
|
|
875
|
-
|
|
876
|
-
if (options.name !== undefined) {
|
|
877
|
-
input.name = options.name;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (requireName && typeof input.name !== "string") {
|
|
881
|
-
throw new CliError("invalid_cli_usage", 'Provide folder name via "--name".');
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
if (!requireName && isEmptyObject(input)) {
|
|
885
|
-
throw new CliError("invalid_cli_usage", 'Provide folder updates via "--name".');
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
return input;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
function buildTriggerInput(
|
|
892
|
-
options: TriggerPayloadCommandOptions,
|
|
893
|
-
mode: "optional" | "required",
|
|
894
|
-
): SetAutomationTriggerInput | undefined {
|
|
895
|
-
const input = {
|
|
896
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
897
|
-
} as Record<string, unknown>;
|
|
898
|
-
|
|
899
|
-
const config = isJsonObject(input.config) ? { ...input.config } : {};
|
|
900
|
-
|
|
901
|
-
if (options.triggerType !== undefined) {
|
|
902
|
-
input.type = options.triggerType;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
if (options.cron !== undefined) {
|
|
906
|
-
config.cronExpression = options.cron;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
if (options.triggerSchema !== undefined) {
|
|
910
|
-
config.schemaId = options.triggerSchema;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
if ((options.triggerField?.length ?? 0) > 0) {
|
|
914
|
-
config.fieldIds = [...(options.triggerField ?? [])];
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
const conditions = readConditions(options.condition, options.conditionJson, "--condition-json");
|
|
918
|
-
if (conditions.length > 0) {
|
|
919
|
-
config.conditions = conditions;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
if (options.webhookEndpointId !== undefined) {
|
|
923
|
-
config.webhookEndpointId = options.webhookEndpointId;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
if (!isEmptyObject(config)) {
|
|
927
|
-
if (Array.isArray(config.conditions)) {
|
|
928
|
-
config.conditions = normalizeAutomationConditions(
|
|
929
|
-
config.conditions,
|
|
930
|
-
"trigger condition",
|
|
931
|
-
{ allowSource: false },
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
input.config = config;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
if (isEmptyObject(input)) {
|
|
939
|
-
if (mode === "optional") {
|
|
940
|
-
return undefined;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
throw new CliError(
|
|
944
|
-
"invalid_cli_usage",
|
|
945
|
-
'Provide trigger input via "--trigger-type", trigger flags, "--raw", or "--raw-file".',
|
|
946
|
-
);
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
return input as SetAutomationTriggerInput;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
async function buildAutomationCreateInput(
|
|
953
|
-
api: AutomationsApi,
|
|
954
|
-
options: AutomationCreateCommandOptions,
|
|
955
|
-
): Promise<CreateAutomationInput> {
|
|
956
|
-
const input = {
|
|
957
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
958
|
-
} as Record<string, unknown>;
|
|
959
|
-
|
|
960
|
-
if (options.name !== undefined) {
|
|
961
|
-
input.name = options.name;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
if (options.description !== undefined) {
|
|
965
|
-
input.description = options.description;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
const folder = await resolveFolderSelector(api, options.folder);
|
|
969
|
-
if (folder) {
|
|
970
|
-
input.folderId = folder.value;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
const trigger = buildTriggerInput(options, "optional");
|
|
974
|
-
if (trigger) {
|
|
975
|
-
input.trigger = trigger;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
if (typeof input.name !== "string" || input.name.trim().length === 0) {
|
|
979
|
-
throw new CliError("invalid_cli_usage", 'Provide automation name via "--name".');
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
return input as CreateAutomationInput;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
async function buildAutomationUpdateInput(
|
|
986
|
-
api: AutomationsApi,
|
|
987
|
-
options: AutomationUpdateCommandOptions,
|
|
988
|
-
): Promise<UpdateAutomationInput> {
|
|
989
|
-
const raw = readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file");
|
|
990
|
-
const { set: rawSet, unset: rawUnset } = splitRawPatchPayload(raw, "raw automation update payload");
|
|
991
|
-
const setPayload: JsonObject = { ...rawSet };
|
|
992
|
-
|
|
993
|
-
if (options.name !== undefined) {
|
|
994
|
-
setPayload.name = options.name;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
if (options.description !== undefined) {
|
|
998
|
-
setPayload.description = options.description;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
const folder = await resolveFolderSelector(api, options.folder);
|
|
1002
|
-
if (folder) {
|
|
1003
|
-
setPayload.folderId = folder.value;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
const unsetPayload = rawUnset.filter((path) => !hasSetPath(setPayload, path));
|
|
1007
|
-
const input: UpdateAutomationInput = {};
|
|
1008
|
-
|
|
1009
|
-
if (!isEmptyObject(setPayload)) {
|
|
1010
|
-
input.set = setPayload;
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
if (unsetPayload.length > 0) {
|
|
1014
|
-
input.unset = [...new Set(unsetPayload)];
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
if (isEmptyObject(input as object)) {
|
|
1018
|
-
throw new CliError(
|
|
1019
|
-
"invalid_cli_usage",
|
|
1020
|
-
'Provide automation updates via "--name", "--description", "--folder", "--raw", or "--raw-file".',
|
|
1021
|
-
);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
return input as UpdateAutomationInput;
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
function buildActionInput(
|
|
1028
|
-
options: ActionMutationCommandOptions,
|
|
1029
|
-
mode: "create" | "update",
|
|
1030
|
-
): MutationAutomationActionInput {
|
|
1031
|
-
const input = {
|
|
1032
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
1033
|
-
} as Record<string, unknown>;
|
|
1034
|
-
|
|
1035
|
-
const config = isJsonObject(input.config) ? { ...input.config } : {};
|
|
1036
|
-
const mappings = mergeMappings(options.map, options.mapJson);
|
|
1037
|
-
const headers = parseStringAssignments(options.header);
|
|
1038
|
-
const variables = parseStringAssignments(options.var);
|
|
1039
|
-
const args = mergeMappings(options.arg, options.argJson);
|
|
1040
|
-
const body = readJsonValueOption(options.body, options.bodyFile, "--body", "--body-file");
|
|
1041
|
-
|
|
1042
|
-
if (options.type !== undefined) {
|
|
1043
|
-
input.type = options.type;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
if (options.name !== undefined) {
|
|
1047
|
-
input.name = options.name;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
if (options.targetSchema !== undefined) {
|
|
1051
|
-
config.targetSchemaId = options.targetSchema;
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
if (options.to !== undefined) {
|
|
1055
|
-
mappings.recipients = options.to;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
if (options.subject !== undefined) {
|
|
1059
|
-
mappings.subject = options.subject;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (options.message !== undefined) {
|
|
1063
|
-
mappings.body = options.message;
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
if (!isEmptyObject(mappings)) {
|
|
1067
|
-
config.fieldMapping = mappings;
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const conditions = readConditions(options.condition, options.conditionJson, "--condition-json");
|
|
1071
|
-
if (conditions.length > 0) {
|
|
1072
|
-
config.conditions = conditions;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
if (options.useTriggerRecord === true) {
|
|
1076
|
-
config.useTriggerRecord = true;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
if (options.method !== undefined) {
|
|
1080
|
-
config.method = options.method.toUpperCase();
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
if (options.url !== undefined) {
|
|
1084
|
-
config.url = options.url;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
if (!isEmptyObject(headers)) {
|
|
1088
|
-
config.headers = headers;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
if (body !== undefined) {
|
|
1092
|
-
config.body = body;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
if (options.contentType !== undefined) {
|
|
1096
|
-
config.contentType = options.contentType;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
if (options.timeoutMs !== undefined) {
|
|
1100
|
-
config.timeoutMs = options.timeoutMs;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
if (!isEmptyObject(variables)) {
|
|
1104
|
-
config.variables = variables;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
if (options.authMode !== undefined) {
|
|
1108
|
-
config.authMode = options.authMode;
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
if (options.connection !== undefined) {
|
|
1112
|
-
config.connectionId = options.connection;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
if (options.plugin !== undefined) {
|
|
1116
|
-
config.pluginId = options.plugin;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
if (!isEmptyObject(args)) {
|
|
1120
|
-
config.pluginArgs = args;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
if (options.documentTemplate !== undefined) {
|
|
1124
|
-
config.documentTemplateId = options.documentTemplate;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
if (options.targetFileField !== undefined) {
|
|
1128
|
-
config.targetFileFieldId = options.targetFileField;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
if (!isEmptyObject(config)) {
|
|
1132
|
-
if (isJsonObject(config.fieldMapping)) {
|
|
1133
|
-
config.fieldMapping = normalizeAutomationFieldMapping(
|
|
1134
|
-
config.fieldMapping,
|
|
1135
|
-
"field mapping",
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
if (Array.isArray(config.conditions)) {
|
|
1140
|
-
config.conditions = normalizeAutomationConditions(
|
|
1141
|
-
config.conditions,
|
|
1142
|
-
"action condition",
|
|
1143
|
-
{ allowSource: true },
|
|
1144
|
-
);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
if (config.useTriggerRecord === true) {
|
|
1148
|
-
delete config.conditions;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
input.config = config;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
if (mode === "create" && typeof input.type !== "string") {
|
|
1155
|
-
throw new CliError("invalid_cli_usage", 'Provide action type via "--type" or "--raw".');
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
if (isEmptyObject(input)) {
|
|
1159
|
-
throw new CliError(
|
|
1160
|
-
"invalid_cli_usage",
|
|
1161
|
-
'Provide action input via flags, "--raw", or "--raw-file".',
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
return input as MutationAutomationActionInput;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
function buildFlowInput(options: FlowSetCommandOptions): SetAutomationFlowInput {
|
|
1169
|
-
const input = {
|
|
1170
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
1171
|
-
} as Record<string, unknown>;
|
|
1172
|
-
|
|
1173
|
-
if ((options.entry?.length ?? 0) > 0) {
|
|
1174
|
-
input.entryActionIds = [...(options.entry ?? [])];
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
if ((options.transition?.length ?? 0) > 0) {
|
|
1178
|
-
input.transitions = (options.transition ?? []).map(parseTransition);
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
if (isEmptyObject(input)) {
|
|
1182
|
-
throw new CliError(
|
|
1183
|
-
"invalid_cli_usage",
|
|
1184
|
-
'Provide flow input via "--entry", "--transition", "--raw", or "--raw-file".',
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
return input as SetAutomationFlowInput;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
function readWebhookFieldMappings(options: WebhookSetCommandOptions): JsonValue[] {
|
|
1192
|
-
const mappings: JsonValue[] = [];
|
|
1193
|
-
|
|
1194
|
-
for (const value of options.fieldMapping ?? []) {
|
|
1195
|
-
mappings.push(parseJsonValue(value, "--field-mapping"));
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
for (const filePath of options.fieldMappingFile ?? []) {
|
|
1199
|
-
mappings.push(
|
|
1200
|
-
parseJsonValue(
|
|
1201
|
-
readTextFile(filePath, "--field-mapping-file"),
|
|
1202
|
-
`--field-mapping-file ${filePath}`,
|
|
1203
|
-
),
|
|
1204
|
-
);
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
return mappings;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
function buildWebhookInput(options: WebhookSetCommandOptions): SetAutomationWebhookInput {
|
|
1211
|
-
const input = {
|
|
1212
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
1213
|
-
} as Record<string, unknown>;
|
|
1214
|
-
|
|
1215
|
-
if (options.active === true) {
|
|
1216
|
-
input.active = true;
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
if (options.idempotencyKeyPath !== undefined) {
|
|
1220
|
-
input.idempotencyKeyJsonPath = options.idempotencyKeyPath;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
if (options.eventTypePath !== undefined) {
|
|
1224
|
-
input.eventTypeJsonPath = options.eventTypePath;
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
if ((options.allowedEventType?.length ?? 0) > 0) {
|
|
1228
|
-
input.allowedEventTypes = [...(options.allowedEventType ?? [])];
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
const fieldMappings = readWebhookFieldMappings(options);
|
|
1232
|
-
if (fieldMappings.length > 0) {
|
|
1233
|
-
input.fieldMappings = fieldMappings;
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
if (isEmptyObject(input)) {
|
|
1237
|
-
throw new CliError(
|
|
1238
|
-
"invalid_cli_usage",
|
|
1239
|
-
'Provide webhook input via flags, "--raw", or "--raw-file".',
|
|
1240
|
-
);
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
return input as SetAutomationWebhookInput;
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function buildConnectionCreateInput(
|
|
1247
|
-
options: ConnectionCreateCommandOptions,
|
|
1248
|
-
): CreateAutomationConnectionInput {
|
|
1249
|
-
const input = {
|
|
1250
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
1251
|
-
} as Record<string, unknown>;
|
|
1252
|
-
|
|
1253
|
-
if (options.name !== undefined) {
|
|
1254
|
-
input.name = options.name;
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
if (options.type !== undefined) {
|
|
1258
|
-
input.type = options.type;
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
if (options.scope !== undefined) {
|
|
1262
|
-
input.scope = options.scope;
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
if (options.ownerMember !== undefined) {
|
|
1266
|
-
input.ownerMemberId = options.ownerMember;
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
const config = readJsonValueOption(options.config, options.configFile, "--config", "--config-file");
|
|
1270
|
-
if (config !== undefined) {
|
|
1271
|
-
if (!isJsonObject(config)) {
|
|
1272
|
-
throw new CliError(
|
|
1273
|
-
"invalid_cli_usage",
|
|
1274
|
-
'Expected "--config" or "--config-file" to contain a JSON object.',
|
|
1275
|
-
);
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
input.config = config;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
const secrets = parseStringAssignments(options.secret);
|
|
1282
|
-
if (!isEmptyObject(secrets)) {
|
|
1283
|
-
input.secret = secrets;
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
if (options.active === true) {
|
|
1287
|
-
input.active = true;
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
if (typeof input.name !== "string") {
|
|
1291
|
-
throw new CliError("invalid_cli_usage", 'Provide connection name via "--name".');
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
if (typeof input.type !== "string") {
|
|
1295
|
-
throw new CliError("invalid_cli_usage", 'Provide connection type via "--type".');
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
if (typeof input.scope !== "string") {
|
|
1299
|
-
throw new CliError("invalid_cli_usage", 'Provide connection scope via "--scope".');
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
return input as CreateAutomationConnectionInput;
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
function buildConnectionUpdateInput(
|
|
1306
|
-
options: ConnectionUpdateCommandOptions,
|
|
1307
|
-
): UpdateAutomationConnectionInput {
|
|
1308
|
-
const input = {
|
|
1309
|
-
...readJsonObjectOption(options.raw, options.rawFile, "--raw", "--raw-file"),
|
|
1310
|
-
} as UpdateAutomationConnectionInput;
|
|
1311
|
-
const setPayload = readRawSetPayload(input as JsonObject, "raw connection update payload");
|
|
1312
|
-
|
|
1313
|
-
for (const value of options.set ?? []) {
|
|
1314
|
-
const assignment = parseKeyValue(value);
|
|
1315
|
-
|
|
1316
|
-
if (assignment.key.startsWith("config.")) {
|
|
1317
|
-
setNestedPath(setPayload, assignment.key.split(".").filter(Boolean), assignment.value);
|
|
1318
|
-
continue;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
setPayload[assignment.key] = assignment.value;
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
if (!isEmptyObject(setPayload)) {
|
|
1325
|
-
input.set = setPayload;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
const unsetPayload = [
|
|
1329
|
-
...readRawUnsetPayload(input as JsonObject, "raw connection update payload"),
|
|
1330
|
-
...(options.unset ?? []),
|
|
1331
|
-
].filter((path) => !hasSetPath(setPayload, path));
|
|
1332
|
-
|
|
1333
|
-
if (unsetPayload.length > 0) {
|
|
1334
|
-
input.unset = [...new Set(unsetPayload)];
|
|
1335
|
-
} else {
|
|
1336
|
-
delete input.unset;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
if (isEmptyObject(input as object)) {
|
|
1340
|
-
throw new CliError(
|
|
1341
|
-
"invalid_cli_usage",
|
|
1342
|
-
'Provide connection updates via "--set", "--unset", "--raw", or "--raw-file".',
|
|
1343
|
-
);
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
return input as UpdateAutomationConnectionInput;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
function addTriggerPayloadOptions(command: Command): Command {
|
|
1350
|
-
return command
|
|
1351
|
-
.option("--trigger-type <type>", "trigger type")
|
|
1352
|
-
.option("--cron <expression>", "cron expression for scheduled triggers")
|
|
1353
|
-
.option("--trigger-schema <uuid>", "schema id for record-based triggers")
|
|
1354
|
-
.option(
|
|
1355
|
-
"--trigger-field <uuid>",
|
|
1356
|
-
"field id for record_changed triggers",
|
|
1357
|
-
collectValues,
|
|
1358
|
-
[],
|
|
1359
|
-
)
|
|
1360
|
-
.option(
|
|
1361
|
-
"--condition <field:comparator:value>",
|
|
1362
|
-
"trigger condition (use field:comparator for comparators without value: chg, ie, ine)",
|
|
1363
|
-
collectValues,
|
|
1364
|
-
[],
|
|
1365
|
-
)
|
|
1366
|
-
.option(
|
|
1367
|
-
"--condition-json <json>",
|
|
1368
|
-
"trigger condition as JSON",
|
|
1369
|
-
collectValues,
|
|
1370
|
-
[],
|
|
1371
|
-
)
|
|
1372
|
-
.option("--webhook-endpoint-id <uuid>", "webhook endpoint id")
|
|
1373
|
-
.option("--raw <json>", "raw trigger JSON payload")
|
|
1374
|
-
.option("--raw-file <path>", "path to raw trigger JSON payload");
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
const ACTION_TYPES_HELP = `
|
|
1378
|
-
Action types and their flags:
|
|
1379
|
-
create_record --target-schema, --map, --map-json
|
|
1380
|
-
update_record --target-schema, --use-trigger-record, --map,
|
|
1381
|
-
--condition, --condition-json
|
|
1382
|
-
search_records --target-schema, --condition, --condition-json
|
|
1383
|
-
send_message --to, --subject, --message
|
|
1384
|
-
(--map / --map-json also accepted for extra keys)
|
|
1385
|
-
http_request --method, --url, --header, --body, --content-type,
|
|
1386
|
-
--timeout-ms, --var, --auth-mode, --connection
|
|
1387
|
-
execute_plugin --plugin, --arg, --arg-json
|
|
1388
|
-
generate_document --document-template, --target-schema, --target-file-field
|
|
1389
|
-
`;
|
|
1390
|
-
|
|
1391
|
-
function addActionPayloadOptions(command: Command): Command {
|
|
1392
|
-
return command
|
|
1393
|
-
.option(
|
|
1394
|
-
"--type <type>",
|
|
1395
|
-
"action type (create_record|update_record|search_records|send_message|http_request|execute_plugin|generate_document)",
|
|
1396
|
-
)
|
|
1397
|
-
.option("--name <name>", "action name")
|
|
1398
|
-
.option("--target-schema <uuid>", "target schema id")
|
|
1399
|
-
.option("--map <field=value>", "field mapping", collectValues, [])
|
|
1400
|
-
.option("--map-json <field=json>", "JSON field mapping", collectValues, [])
|
|
1401
|
-
.option(
|
|
1402
|
-
"--condition <field:comparator:value>",
|
|
1403
|
-
"action condition (use field:comparator for comparators without value: ie, ine)",
|
|
1404
|
-
collectValues,
|
|
1405
|
-
[],
|
|
1406
|
-
)
|
|
1407
|
-
.option("--condition-json <json>", "action condition as JSON", collectValues, [])
|
|
1408
|
-
.option("--use-trigger-record", "use the triggering record")
|
|
1409
|
-
.option("--method <method>", "HTTP method")
|
|
1410
|
-
.option("--url <url>", "HTTP target URL")
|
|
1411
|
-
.option("--header <key=value>", "HTTP header", collectValues, [])
|
|
1412
|
-
.option("--body <json>", "HTTP request body as JSON")
|
|
1413
|
-
.option("--body-file <path>", "path to HTTP request body JSON")
|
|
1414
|
-
.option("--content-type <type>", "HTTP content type")
|
|
1415
|
-
.option(
|
|
1416
|
-
"--timeout-ms <n>",
|
|
1417
|
-
"HTTP timeout in milliseconds",
|
|
1418
|
-
parseNonNegativeInteger,
|
|
1419
|
-
)
|
|
1420
|
-
.option("--var <key=value>", "HTTP variable", collectValues, [])
|
|
1421
|
-
.option("--auth-mode <mode>", "HTTP auth mode")
|
|
1422
|
-
.option("--connection <uuid>", "connection id for HTTP auth")
|
|
1423
|
-
.option("--plugin <id>", "plugin id for execute_plugin")
|
|
1424
|
-
.option("--arg <key=value>", "plugin argument", collectValues, [])
|
|
1425
|
-
.option("--arg-json <key=json>", "plugin argument as JSON", collectValues, [])
|
|
1426
|
-
.option("--to <recipient>", "send_message recipient (maps to fieldMapping.recipients)")
|
|
1427
|
-
.option("--subject <text>", "send_message subject")
|
|
1428
|
-
.option("--message <text>", "send_message body text (maps to fieldMapping.body)")
|
|
1429
|
-
.option("--document-template <uuid>", "generate_document template id")
|
|
1430
|
-
.option("--target-file-field <uuid>", "generate_document target FileField id")
|
|
1431
|
-
.option("--raw <json>", "raw action JSON payload")
|
|
1432
|
-
.option("--raw-file <path>", "path to raw action JSON payload")
|
|
1433
|
-
.addHelpText("after", ACTION_TYPES_HELP);
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
export function registerAutomationsCommands(
|
|
1437
|
-
program: Command,
|
|
1438
|
-
options: RegisterAutomationsCommandsOptions = {},
|
|
1439
|
-
): void {
|
|
1440
|
-
const filePath = options.configPath ?? defaultConfigPath;
|
|
1441
|
-
const stdout = getCommandIo(options.stdout);
|
|
1442
|
-
const automations = program
|
|
1443
|
-
.command("automations")
|
|
1444
|
-
.description("Manage automations, triggers, actions, and connections");
|
|
1445
|
-
|
|
1446
|
-
const folders = automations
|
|
1447
|
-
.command("folders")
|
|
1448
|
-
.description("Manage automation folders (flat: a folder only has a name, no order or properties)");
|
|
1449
|
-
|
|
1450
|
-
folders
|
|
1451
|
-
.command("list")
|
|
1452
|
-
.description("List automation folders (non-root folders plus the root bucket, whose name is null)")
|
|
1453
|
-
.action(async function action(this: Command) {
|
|
1454
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1455
|
-
const data = await api.listFolders();
|
|
1456
|
-
|
|
1457
|
-
if (json) {
|
|
1458
|
-
emitJson(stdout, "automations folders list", data, filePath);
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
printTable(folderRows(data), ["id", "name", "automationCount"], stdout);
|
|
1463
|
-
});
|
|
1464
|
-
|
|
1465
|
-
folders
|
|
1466
|
-
.command("create")
|
|
1467
|
-
.description("Create an automation folder")
|
|
1468
|
-
.requiredOption("--name <name>", "folder name")
|
|
1469
|
-
.action(async function action(this: Command, commandOptions: FolderMutationCommandOptions) {
|
|
1470
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1471
|
-
ensureWritable("automations folders create", readOnly, "write");
|
|
1472
|
-
const data = await api.createFolder(buildFolderMutationInput(commandOptions, true));
|
|
1473
|
-
|
|
1474
|
-
if (json) {
|
|
1475
|
-
emitJson(stdout, "automations folders create", data, filePath);
|
|
1476
|
-
return;
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
writeLine(stdout, `Created automation folder "${String(data.name ?? commandOptions.name)}".`);
|
|
1480
|
-
});
|
|
1481
|
-
|
|
1482
|
-
folders
|
|
1483
|
-
.command("update")
|
|
1484
|
-
.description("Update an automation folder")
|
|
1485
|
-
.argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector)
|
|
1486
|
-
.option("--name <name>", "folder name")
|
|
1487
|
-
.action(async function action(
|
|
1488
|
-
this: Command,
|
|
1489
|
-
folder: NormalizedUuidOrSlug,
|
|
1490
|
-
commandOptions: FolderMutationCommandOptions,
|
|
1491
|
-
) {
|
|
1492
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1493
|
-
ensureWritable("automations folders update", readOnly, "write");
|
|
1494
|
-
const resolvedFolder = await resolveFolderSelector(api, folder);
|
|
1495
|
-
|
|
1496
|
-
if (!resolvedFolder) {
|
|
1497
|
-
throw new CliError("invalid_cli_usage", `Folder "${folder.value}" was not found.`);
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
const data = await api.updateFolder(
|
|
1501
|
-
resolvedFolder,
|
|
1502
|
-
buildFolderMutationInput(commandOptions, false),
|
|
1503
|
-
);
|
|
1504
|
-
|
|
1505
|
-
if (json) {
|
|
1506
|
-
emitJson(stdout, "automations folders update", data, filePath);
|
|
1507
|
-
return;
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
writeLine(stdout, `Updated automation folder "${String(data.name ?? folder.value)}".`);
|
|
1511
|
-
});
|
|
1512
|
-
|
|
1513
|
-
folders
|
|
1514
|
-
.command("delete")
|
|
1515
|
-
.description("Delete an automation folder")
|
|
1516
|
-
.argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector)
|
|
1517
|
-
.action(async function action(this: Command, folder: NormalizedUuidOrSlug) {
|
|
1518
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1519
|
-
ensureWritable("automations folders delete", readOnly, "destructive");
|
|
1520
|
-
const resolvedFolder = await resolveFolderSelector(api, folder);
|
|
1521
|
-
|
|
1522
|
-
if (!resolvedFolder) {
|
|
1523
|
-
throw new CliError("invalid_cli_usage", `Folder "${folder.value}" was not found.`);
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
const data = await api.deleteFolder(resolvedFolder);
|
|
1527
|
-
|
|
1528
|
-
if (json) {
|
|
1529
|
-
emitJson(stdout, "automations folders delete", data, filePath);
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
writeLine(stdout, `Deleted automation folder "${folder.value}".`);
|
|
1534
|
-
});
|
|
1535
|
-
|
|
1536
|
-
folders
|
|
1537
|
-
.command("move")
|
|
1538
|
-
.description("Move an automation into a folder")
|
|
1539
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1540
|
-
.requiredOption(
|
|
1541
|
-
"--to <folder>",
|
|
1542
|
-
"target folder UUID or exact name (use the root folder to move it out of any folder)",
|
|
1543
|
-
parseUuidOrSlugSelector,
|
|
1544
|
-
)
|
|
1545
|
-
.action(async function action(
|
|
1546
|
-
this: Command,
|
|
1547
|
-
automation: NormalizedUuidOrSlug,
|
|
1548
|
-
commandOptions: FolderMoveCommandOptions,
|
|
1549
|
-
) {
|
|
1550
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1551
|
-
ensureWritable("automations folders move", readOnly, "write");
|
|
1552
|
-
|
|
1553
|
-
if (!commandOptions.to) {
|
|
1554
|
-
throw new CliError("invalid_cli_usage", 'Provide target folder via "--to".');
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
const targetFolder = await resolveFolderSelector(api, commandOptions.to);
|
|
1558
|
-
|
|
1559
|
-
if (!targetFolder) {
|
|
1560
|
-
throw new CliError(
|
|
1561
|
-
"invalid_cli_usage",
|
|
1562
|
-
`Folder "${commandOptions.to.value}" was not found.`,
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
const data = await api.moveAutomation({
|
|
1567
|
-
automationId: automation.value,
|
|
1568
|
-
targetFolderId: targetFolder.value,
|
|
1569
|
-
});
|
|
1570
|
-
|
|
1571
|
-
if (json) {
|
|
1572
|
-
emitJson(stdout, "automations folders move", data, filePath);
|
|
1573
|
-
return;
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
writeLine(stdout, `Moved automation "${automation.value}" to folder "${targetFolder.value}".`);
|
|
1577
|
-
});
|
|
1578
|
-
|
|
1579
|
-
automations
|
|
1580
|
-
.command("list")
|
|
1581
|
-
.description("List automations")
|
|
1582
|
-
.option("--folder <folder>", "filter by folder UUID or exact name", parseUuidOrSlugSelector)
|
|
1583
|
-
.option("--status <status>", "filter by status")
|
|
1584
|
-
.option("--trigger-type <type>", "filter by trigger type")
|
|
1585
|
-
.option("--action-type <type>", "filter by action type")
|
|
1586
|
-
.option("--plugin <id>", "filter by plugin id")
|
|
1587
|
-
.option("--search <text>", "search text")
|
|
1588
|
-
.action(async function action(this: Command, commandOptions: AutomationListCommandOptions) {
|
|
1589
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1590
|
-
const folder = await resolveFolderSelector(api, commandOptions.folder);
|
|
1591
|
-
const data = await api.listAutomations({
|
|
1592
|
-
folderId: folder?.value,
|
|
1593
|
-
status: commandOptions.status,
|
|
1594
|
-
triggerType: commandOptions.triggerType,
|
|
1595
|
-
actionType: commandOptions.actionType,
|
|
1596
|
-
pluginId: commandOptions.plugin,
|
|
1597
|
-
search: commandOptions.search,
|
|
1598
|
-
});
|
|
1599
|
-
|
|
1600
|
-
if (json) {
|
|
1601
|
-
emitJson(stdout, "automations list", data, filePath);
|
|
1602
|
-
return;
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
printTable(
|
|
1606
|
-
automationRows(data),
|
|
1607
|
-
["id", "name", "status", "triggerType", "actionCount", ...FOLDER_TABLE_KEYS],
|
|
1608
|
-
stdout,
|
|
1609
|
-
);
|
|
1610
|
-
});
|
|
1611
|
-
|
|
1612
|
-
automations
|
|
1613
|
-
.command("get")
|
|
1614
|
-
.description("Get an automation")
|
|
1615
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1616
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1617
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1618
|
-
const data = await api.getAutomation(automation);
|
|
1619
|
-
|
|
1620
|
-
if (json) {
|
|
1621
|
-
emitJson(stdout, "automations get", data, filePath);
|
|
1622
|
-
return;
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
emitTable(stdout, data);
|
|
1626
|
-
});
|
|
1627
|
-
|
|
1628
|
-
addTriggerPayloadOptions(
|
|
1629
|
-
automations
|
|
1630
|
-
.command("create")
|
|
1631
|
-
.description("Create an automation")
|
|
1632
|
-
.requiredOption("--name <name>", "automation name")
|
|
1633
|
-
.option("--description <text>", "automation description")
|
|
1634
|
-
.option("--folder <folder>", "folder UUID or exact name", parseUuidOrSlugSelector),
|
|
1635
|
-
).action(async function action(
|
|
1636
|
-
this: Command,
|
|
1637
|
-
commandOptions: AutomationCreateCommandOptions,
|
|
1638
|
-
) {
|
|
1639
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1640
|
-
ensureWritable("automations create", readOnly, "write");
|
|
1641
|
-
const data = await api.createAutomation(await buildAutomationCreateInput(api, commandOptions));
|
|
1642
|
-
|
|
1643
|
-
if (json) {
|
|
1644
|
-
emitJson(stdout, "automations create", data, filePath);
|
|
1645
|
-
return;
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
writeLine(stdout, `Created automation "${String(data.name ?? commandOptions.name)}".`);
|
|
1649
|
-
});
|
|
1650
|
-
|
|
1651
|
-
automations
|
|
1652
|
-
.command("update")
|
|
1653
|
-
.description("Update an automation")
|
|
1654
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1655
|
-
.option("--name <name>", "automation name")
|
|
1656
|
-
.option("--description <text>", "automation description")
|
|
1657
|
-
.option("--folder <folder>", "folder UUID or exact name", parseUuidOrSlugSelector)
|
|
1658
|
-
.option("--raw <json>", "raw automation JSON payload")
|
|
1659
|
-
.option("--raw-file <path>", "path to raw automation JSON payload")
|
|
1660
|
-
.action(async function action(
|
|
1661
|
-
this: Command,
|
|
1662
|
-
automation: NormalizedUuidOrSlug,
|
|
1663
|
-
commandOptions: AutomationUpdateCommandOptions,
|
|
1664
|
-
) {
|
|
1665
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1666
|
-
ensureWritable("automations update", readOnly, "write");
|
|
1667
|
-
const data = await api.updateAutomation(
|
|
1668
|
-
automation,
|
|
1669
|
-
await buildAutomationUpdateInput(api, commandOptions),
|
|
1670
|
-
);
|
|
1671
|
-
|
|
1672
|
-
if (json) {
|
|
1673
|
-
emitJson(stdout, "automations update", data, filePath);
|
|
1674
|
-
return;
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
writeLine(stdout, `Updated automation "${automation.value}".`);
|
|
1678
|
-
});
|
|
1679
|
-
|
|
1680
|
-
automations
|
|
1681
|
-
.command("delete")
|
|
1682
|
-
.description("Delete an automation")
|
|
1683
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1684
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1685
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1686
|
-
ensureWritable("automations delete", readOnly, "destructive");
|
|
1687
|
-
const data = await api.deleteAutomation(automation);
|
|
1688
|
-
|
|
1689
|
-
if (json) {
|
|
1690
|
-
emitJson(stdout, "automations delete", data, filePath);
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
writeLine(stdout, `Deleted automation "${automation.value}".`);
|
|
1695
|
-
});
|
|
1696
|
-
|
|
1697
|
-
automations
|
|
1698
|
-
.command("activate")
|
|
1699
|
-
.description("Activate an automation")
|
|
1700
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1701
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1702
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1703
|
-
ensureWritable("automations activate", readOnly, "write");
|
|
1704
|
-
const data = await api.activateAutomation(automation);
|
|
1705
|
-
|
|
1706
|
-
if (json) {
|
|
1707
|
-
emitJson(stdout, "automations activate", data, filePath);
|
|
1708
|
-
return;
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
writeLine(stdout, `Activated automation "${automation.value}".`);
|
|
1712
|
-
});
|
|
1713
|
-
|
|
1714
|
-
automations
|
|
1715
|
-
.command("deactivate")
|
|
1716
|
-
.description("Deactivate an automation")
|
|
1717
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1718
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1719
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1720
|
-
ensureWritable("automations deactivate", readOnly, "write");
|
|
1721
|
-
const data = await api.deactivateAutomation(automation);
|
|
1722
|
-
|
|
1723
|
-
if (json) {
|
|
1724
|
-
emitJson(stdout, "automations deactivate", data, filePath);
|
|
1725
|
-
return;
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
writeLine(stdout, `Deactivated automation "${automation.value}".`);
|
|
1729
|
-
});
|
|
1730
|
-
|
|
1731
|
-
const trigger = automations.command("trigger").description("Manage automation triggers");
|
|
1732
|
-
|
|
1733
|
-
trigger
|
|
1734
|
-
.command("get")
|
|
1735
|
-
.description("Get the trigger for an automation")
|
|
1736
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1737
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1738
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1739
|
-
const data = await api.getTrigger(automation);
|
|
1740
|
-
|
|
1741
|
-
if (json) {
|
|
1742
|
-
emitJson(stdout, "automations trigger get", data, filePath);
|
|
1743
|
-
return;
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
emitTable(stdout, data);
|
|
1747
|
-
});
|
|
1748
|
-
|
|
1749
|
-
addTriggerPayloadOptions(
|
|
1750
|
-
trigger
|
|
1751
|
-
.command("set")
|
|
1752
|
-
.description("Set the trigger for an automation")
|
|
1753
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector),
|
|
1754
|
-
).action(async function action(
|
|
1755
|
-
this: Command,
|
|
1756
|
-
automation: NormalizedUuidOrSlug,
|
|
1757
|
-
commandOptions: TriggerPayloadCommandOptions,
|
|
1758
|
-
) {
|
|
1759
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1760
|
-
ensureWritable("automations trigger set", readOnly, "write");
|
|
1761
|
-
const data = await api.setTrigger(
|
|
1762
|
-
automation,
|
|
1763
|
-
buildTriggerInput(commandOptions, "required") as SetAutomationTriggerInput,
|
|
1764
|
-
);
|
|
1765
|
-
|
|
1766
|
-
if (json) {
|
|
1767
|
-
emitJson(stdout, "automations trigger set", data, filePath);
|
|
1768
|
-
return;
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
writeLine(stdout, `Updated trigger for automation "${automation.value}".`);
|
|
1772
|
-
});
|
|
1773
|
-
|
|
1774
|
-
const actions = automations.command("actions").description("Manage automation actions");
|
|
1775
|
-
|
|
1776
|
-
actions
|
|
1777
|
-
.command("list")
|
|
1778
|
-
.description("List automation actions")
|
|
1779
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1780
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1781
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1782
|
-
const data = await api.listActions(automation);
|
|
1783
|
-
|
|
1784
|
-
if (json) {
|
|
1785
|
-
emitJson(stdout, "automations actions list", data, filePath);
|
|
1786
|
-
return;
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
printTable(actionRows(data), ["id", "name", "type"], stdout);
|
|
1790
|
-
});
|
|
1791
|
-
|
|
1792
|
-
addActionPayloadOptions(
|
|
1793
|
-
actions
|
|
1794
|
-
.command("add")
|
|
1795
|
-
.description("Add an action to an automation")
|
|
1796
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector),
|
|
1797
|
-
).action(async function action(
|
|
1798
|
-
this: Command,
|
|
1799
|
-
automation: NormalizedUuidOrSlug,
|
|
1800
|
-
commandOptions: ActionMutationCommandOptions,
|
|
1801
|
-
) {
|
|
1802
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1803
|
-
ensureWritable("automations actions add", readOnly, "write");
|
|
1804
|
-
const data = await api.addAction(automation, buildActionInput(commandOptions, "create"));
|
|
1805
|
-
|
|
1806
|
-
if (json) {
|
|
1807
|
-
emitJson(stdout, "automations actions add", data, filePath);
|
|
1808
|
-
return;
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
writeLine(stdout, `Added action to automation "${automation.value}".`);
|
|
1812
|
-
});
|
|
1813
|
-
|
|
1814
|
-
addActionPayloadOptions(
|
|
1815
|
-
actions
|
|
1816
|
-
.command("update")
|
|
1817
|
-
.description("Update an automation action")
|
|
1818
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1819
|
-
.argument("<action>", "action UUID"),
|
|
1820
|
-
).action(async function action(
|
|
1821
|
-
this: Command,
|
|
1822
|
-
automation: NormalizedUuidOrSlug,
|
|
1823
|
-
actionId: string,
|
|
1824
|
-
commandOptions: ActionMutationCommandOptions,
|
|
1825
|
-
) {
|
|
1826
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1827
|
-
ensureWritable("automations actions update", readOnly, "write");
|
|
1828
|
-
const data = await api.updateAction(automation, actionId, buildActionInput(commandOptions, "update"));
|
|
1829
|
-
|
|
1830
|
-
if (json) {
|
|
1831
|
-
emitJson(stdout, "automations actions update", data, filePath);
|
|
1832
|
-
return;
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
writeLine(stdout, `Updated action "${actionId}" on automation "${automation.value}".`);
|
|
1836
|
-
});
|
|
1837
|
-
|
|
1838
|
-
actions
|
|
1839
|
-
.command("delete")
|
|
1840
|
-
.description("Delete an automation action")
|
|
1841
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1842
|
-
.argument("<action>", "action UUID")
|
|
1843
|
-
.action(async function action(
|
|
1844
|
-
this: Command,
|
|
1845
|
-
automation: NormalizedUuidOrSlug,
|
|
1846
|
-
actionId: string,
|
|
1847
|
-
) {
|
|
1848
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1849
|
-
ensureWritable("automations actions delete", readOnly, "destructive");
|
|
1850
|
-
const data = await api.deleteAction(automation, actionId);
|
|
1851
|
-
|
|
1852
|
-
if (json) {
|
|
1853
|
-
emitJson(stdout, "automations actions delete", data, filePath);
|
|
1854
|
-
return;
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
writeLine(stdout, `Deleted action "${actionId}" from automation "${automation.value}".`);
|
|
1858
|
-
});
|
|
1859
|
-
|
|
1860
|
-
const flow = automations.command("flow").description("Manage automation flow wiring");
|
|
1861
|
-
|
|
1862
|
-
flow
|
|
1863
|
-
.command("get")
|
|
1864
|
-
.description("Get the flow for an automation")
|
|
1865
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1866
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1867
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1868
|
-
const data = await api.getFlow(automation);
|
|
1869
|
-
|
|
1870
|
-
if (json) {
|
|
1871
|
-
emitJson(stdout, "automations flow get", data, filePath);
|
|
1872
|
-
return;
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
emitTable(stdout, data);
|
|
1876
|
-
});
|
|
1877
|
-
|
|
1878
|
-
flow
|
|
1879
|
-
.command("set")
|
|
1880
|
-
.description("Set the flow for an automation")
|
|
1881
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1882
|
-
.option("--entry <actionId>", "entry action id", collectValues, [])
|
|
1883
|
-
.option("--transition <from:to>", "transition", collectValues, [])
|
|
1884
|
-
.option("--raw <json>", "raw flow JSON payload")
|
|
1885
|
-
.option("--raw-file <path>", "path to raw flow JSON payload")
|
|
1886
|
-
.action(async function action(
|
|
1887
|
-
this: Command,
|
|
1888
|
-
automation: NormalizedUuidOrSlug,
|
|
1889
|
-
commandOptions: FlowSetCommandOptions,
|
|
1890
|
-
) {
|
|
1891
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1892
|
-
ensureWritable("automations flow set", readOnly, "write");
|
|
1893
|
-
const data = await api.setFlow(automation, buildFlowInput(commandOptions));
|
|
1894
|
-
|
|
1895
|
-
if (json) {
|
|
1896
|
-
emitJson(stdout, "automations flow set", data, filePath);
|
|
1897
|
-
return;
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
writeLine(stdout, `Updated flow for automation "${automation.value}".`);
|
|
1901
|
-
});
|
|
1902
|
-
|
|
1903
|
-
const webhook = automations.command("webhook").description("Manage automation webhook config");
|
|
1904
|
-
|
|
1905
|
-
webhook
|
|
1906
|
-
.command("get")
|
|
1907
|
-
.description("Get webhook configuration for an automation")
|
|
1908
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1909
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1910
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1911
|
-
const data = await api.getWebhook(automation);
|
|
1912
|
-
|
|
1913
|
-
if (json) {
|
|
1914
|
-
emitJson(stdout, "automations webhook get", data, filePath);
|
|
1915
|
-
return;
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
emitTable(stdout, data);
|
|
1919
|
-
});
|
|
1920
|
-
|
|
1921
|
-
webhook
|
|
1922
|
-
.command("set")
|
|
1923
|
-
.description("Set webhook configuration for an automation")
|
|
1924
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1925
|
-
.option("--active", "activate webhook processing")
|
|
1926
|
-
.option("--idempotency-key-path <jsonpath>", "idempotency key JSONPath")
|
|
1927
|
-
.option("--event-type-path <jsonpath>", "event type JSONPath")
|
|
1928
|
-
.option("--allowed-event-type <value>", "allowed event type", collectValues, [])
|
|
1929
|
-
.option("--field-mapping <json>", "field mapping JSON", collectValues, [])
|
|
1930
|
-
.option("--field-mapping-file <path>", "path to field mapping JSON", collectValues, [])
|
|
1931
|
-
.option("--raw <json>", "raw webhook JSON payload")
|
|
1932
|
-
.option("--raw-file <path>", "path to raw webhook JSON payload")
|
|
1933
|
-
.action(async function action(
|
|
1934
|
-
this: Command,
|
|
1935
|
-
automation: NormalizedUuidOrSlug,
|
|
1936
|
-
commandOptions: WebhookSetCommandOptions,
|
|
1937
|
-
) {
|
|
1938
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1939
|
-
ensureWritable("automations webhook set", readOnly, "write");
|
|
1940
|
-
const data = await api.setWebhook(automation, buildWebhookInput(commandOptions));
|
|
1941
|
-
|
|
1942
|
-
if (json) {
|
|
1943
|
-
emitJson(stdout, "automations webhook set", data, filePath);
|
|
1944
|
-
return;
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
writeLine(stdout, `Updated webhook for automation "${automation.value}".`);
|
|
1948
|
-
});
|
|
1949
|
-
|
|
1950
|
-
webhook
|
|
1951
|
-
.command("delete")
|
|
1952
|
-
.description("Delete webhook configuration for an automation")
|
|
1953
|
-
.argument("<automation>", "automation UUID", parseUuidOrSlugSelector)
|
|
1954
|
-
.action(async function action(this: Command, automation: NormalizedUuidOrSlug) {
|
|
1955
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
1956
|
-
ensureWritable("automations webhook delete", readOnly, "destructive");
|
|
1957
|
-
const data = await api.deleteWebhook(automation);
|
|
1958
|
-
|
|
1959
|
-
if (json) {
|
|
1960
|
-
emitJson(stdout, "automations webhook delete", data, filePath);
|
|
1961
|
-
return;
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
writeLine(stdout, `Deleted webhook for automation "${automation.value}".`);
|
|
1965
|
-
});
|
|
1966
|
-
|
|
1967
|
-
const plugins = automations.command("plugins").description("Inspect automation plugins");
|
|
1968
|
-
|
|
1969
|
-
plugins
|
|
1970
|
-
.command("list")
|
|
1971
|
-
.description("List automation plugins")
|
|
1972
|
-
.action(async function action(this: Command) {
|
|
1973
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1974
|
-
const data = await api.listPlugins();
|
|
1975
|
-
|
|
1976
|
-
if (json) {
|
|
1977
|
-
emitJson(stdout, "automations plugins list", data, filePath);
|
|
1978
|
-
return;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
printTable(pluginRows(data), ["id", "name", "version"], stdout);
|
|
1982
|
-
});
|
|
1983
|
-
|
|
1984
|
-
plugins
|
|
1985
|
-
.command("get")
|
|
1986
|
-
.description("Get an automation plugin")
|
|
1987
|
-
.argument("<plugin>", "plugin id")
|
|
1988
|
-
.action(async function action(this: Command, pluginId: string) {
|
|
1989
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
1990
|
-
const data = await api.getPlugin(pluginId);
|
|
1991
|
-
|
|
1992
|
-
if (json) {
|
|
1993
|
-
emitJson(stdout, "automations plugins get", data, filePath);
|
|
1994
|
-
return;
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
emitTable(stdout, data);
|
|
1998
|
-
});
|
|
1999
|
-
|
|
2000
|
-
const connections = automations
|
|
2001
|
-
.command("connections")
|
|
2002
|
-
.description("Manage automation connections");
|
|
2003
|
-
|
|
2004
|
-
connections
|
|
2005
|
-
.command("list")
|
|
2006
|
-
.description("List automation connections")
|
|
2007
|
-
.action(async function action(this: Command) {
|
|
2008
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
2009
|
-
const data = await api.listConnections();
|
|
2010
|
-
|
|
2011
|
-
if (json) {
|
|
2012
|
-
emitJson(stdout, "automations connections list", data, filePath);
|
|
2013
|
-
return;
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
printTable(connectionRows(data), ["id", "name", "type", "scope", "active"], stdout);
|
|
2017
|
-
});
|
|
2018
|
-
|
|
2019
|
-
connections
|
|
2020
|
-
.command("get")
|
|
2021
|
-
.description("Get an automation connection")
|
|
2022
|
-
.argument("<connection>", "connection UUID", parseUuidOrSlugSelector)
|
|
2023
|
-
.action(async function action(this: Command, connection: NormalizedUuidOrSlug) {
|
|
2024
|
-
const { api, json } = createAutomationsApi(this, filePath);
|
|
2025
|
-
const data = await api.getConnection(connection);
|
|
2026
|
-
|
|
2027
|
-
if (json) {
|
|
2028
|
-
emitJson(stdout, "automations connections get", data, filePath);
|
|
2029
|
-
return;
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
emitTable(stdout, data);
|
|
2033
|
-
});
|
|
2034
|
-
|
|
2035
|
-
connections
|
|
2036
|
-
.command("create")
|
|
2037
|
-
.description("Create an automation connection")
|
|
2038
|
-
.requiredOption("--name <name>", "connection name")
|
|
2039
|
-
.requiredOption("--type <type>", "connection type")
|
|
2040
|
-
.requiredOption("--scope <scope>", "connection scope")
|
|
2041
|
-
.option("--owner-member <uuid>", "owner member id")
|
|
2042
|
-
.option("--config <json>", "connection config JSON object")
|
|
2043
|
-
.option("--config-file <path>", "path to connection config JSON object")
|
|
2044
|
-
.option("--secret <key=value>", "connection secret", collectValues, [])
|
|
2045
|
-
.option("--active", "activate connection")
|
|
2046
|
-
.option("--raw <json>", "raw connection JSON payload")
|
|
2047
|
-
.option("--raw-file <path>", "path to raw connection JSON payload")
|
|
2048
|
-
.action(async function action(this: Command, commandOptions: ConnectionCreateCommandOptions) {
|
|
2049
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
2050
|
-
ensureWritable("automations connections create", readOnly, "write");
|
|
2051
|
-
const data = await api.createConnection(buildConnectionCreateInput(commandOptions));
|
|
2052
|
-
|
|
2053
|
-
if (json) {
|
|
2054
|
-
emitJson(stdout, "automations connections create", data, filePath);
|
|
2055
|
-
return;
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
writeLine(stdout, `Created connection "${String(data.name ?? commandOptions.name)}".`);
|
|
2059
|
-
});
|
|
2060
|
-
|
|
2061
|
-
connections
|
|
2062
|
-
.command("update")
|
|
2063
|
-
.description("Update an automation connection")
|
|
2064
|
-
.argument("<connection>", "connection UUID", parseUuidOrSlugSelector)
|
|
2065
|
-
.option("--set <path=value>", "set a path to a string value", collectValues, [])
|
|
2066
|
-
.option("--unset <path>", "unset a path", collectValues, [])
|
|
2067
|
-
.option("--raw <json>", "raw connection patch JSON payload")
|
|
2068
|
-
.option("--raw-file <path>", "path to raw connection patch JSON payload")
|
|
2069
|
-
.action(async function action(
|
|
2070
|
-
this: Command,
|
|
2071
|
-
connection: NormalizedUuidOrSlug,
|
|
2072
|
-
commandOptions: ConnectionUpdateCommandOptions,
|
|
2073
|
-
) {
|
|
2074
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
2075
|
-
ensureWritable("automations connections update", readOnly, "write");
|
|
2076
|
-
const data = await api.updateConnection(connection, buildConnectionUpdateInput(commandOptions));
|
|
2077
|
-
|
|
2078
|
-
if (json) {
|
|
2079
|
-
emitJson(stdout, "automations connections update", data, filePath);
|
|
2080
|
-
return;
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
writeLine(stdout, `Updated connection "${connection.value}".`);
|
|
2084
|
-
});
|
|
2085
|
-
|
|
2086
|
-
connections
|
|
2087
|
-
.command("delete")
|
|
2088
|
-
.description("Delete an automation connection")
|
|
2089
|
-
.argument("<connection>", "connection UUID", parseUuidOrSlugSelector)
|
|
2090
|
-
.action(async function action(this: Command, connection: NormalizedUuidOrSlug) {
|
|
2091
|
-
const { api, json, readOnly } = createAutomationsApi(this, filePath);
|
|
2092
|
-
ensureWritable("automations connections delete", readOnly, "destructive");
|
|
2093
|
-
const data = await api.deleteConnection(connection);
|
|
2094
|
-
|
|
2095
|
-
if (json) {
|
|
2096
|
-
emitJson(stdout, "automations connections delete", data, filePath);
|
|
2097
|
-
return;
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
writeLine(stdout, `Deleted connection "${connection.value}".`);
|
|
2101
|
-
});
|
|
2102
|
-
}
|