@bpmnkit/cli 0.0.9
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 +113 -0
- package/dist/args.js +101 -0
- package/dist/client.js +19 -0
- package/dist/color.js +50 -0
- package/dist/commands/admin-shared.js +180 -0
- package/dist/commands/bpmn.js +143 -0
- package/dist/commands/completion.js +52 -0
- package/dist/commands/connector.js +172 -0
- package/dist/commands/index.js +47 -0
- package/dist/commands/profile.js +325 -0
- package/dist/commands/relations.js +28 -0
- package/dist/commands/settings.js +138 -0
- package/dist/commands/shared.js +196 -0
- package/dist/commands/worker.js +135 -0
- package/dist/completion.js +89 -0
- package/dist/generated/admin-commands.js +407 -0
- package/dist/generated/commands.js +2101 -0
- package/dist/help.js +117 -0
- package/dist/index.js +4 -0
- package/dist/output.js +262 -0
- package/dist/profile-tui.js +229 -0
- package/dist/profile.js +103 -0
- package/dist/run.js +210 -0
- package/dist/settings-tui.js +195 -0
- package/dist/tui.js +2544 -0
- package/dist/types.js +2 -0
- package/package.json +32 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { clearAuditLog, getActiveName, getAuditLog, getSettings, saveSettings, } from "@bpmnkit/profiles";
|
|
2
|
+
export const settingsGroup = {
|
|
3
|
+
name: "settings",
|
|
4
|
+
description: "Manage CLI settings and audit log",
|
|
5
|
+
commands: [
|
|
6
|
+
{
|
|
7
|
+
name: "show",
|
|
8
|
+
description: "Show current settings",
|
|
9
|
+
examples: [{ description: "Show settings", command: "casen settings show" }],
|
|
10
|
+
async run(ctx) {
|
|
11
|
+
const settings = getSettings();
|
|
12
|
+
ctx.output.info(`audit-log-size: ${settings.auditLogSize}`);
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "set",
|
|
17
|
+
description: "Change a setting",
|
|
18
|
+
args: [
|
|
19
|
+
{
|
|
20
|
+
name: "key",
|
|
21
|
+
description: "Setting name: audit-log-size",
|
|
22
|
+
required: true,
|
|
23
|
+
enum: ["audit-log-size"],
|
|
24
|
+
},
|
|
25
|
+
{ name: "value", description: "New value", required: true },
|
|
26
|
+
],
|
|
27
|
+
examples: [
|
|
28
|
+
{ description: "Keep last 25 actions", command: "casen settings set audit-log-size 25" },
|
|
29
|
+
{ description: "Disable audit log", command: "casen settings set audit-log-size 0" },
|
|
30
|
+
],
|
|
31
|
+
async run(ctx) {
|
|
32
|
+
const key = ctx.positional[0];
|
|
33
|
+
const rawValue = ctx.positional[1];
|
|
34
|
+
if (!key)
|
|
35
|
+
throw new Error("Missing required argument: <key>");
|
|
36
|
+
if (rawValue === undefined)
|
|
37
|
+
throw new Error("Missing required argument: <value>");
|
|
38
|
+
if (key === "audit-log-size") {
|
|
39
|
+
const n = Number(rawValue);
|
|
40
|
+
if (Number.isNaN(n) || n < 0)
|
|
41
|
+
throw new Error("audit-log-size must be a non-negative integer");
|
|
42
|
+
saveSettings({ auditLogSize: Math.floor(n) });
|
|
43
|
+
ctx.output.ok(`audit-log-size = ${Math.floor(n)}`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
throw new Error(`Unknown setting: "${key}". Valid: audit-log-size`);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "audit-log",
|
|
52
|
+
aliases: ["log"],
|
|
53
|
+
description: "Show the audit log for the active (or specified) profile",
|
|
54
|
+
flags: [
|
|
55
|
+
{ name: "profile", description: "Profile name (defaults to active)", type: "string" },
|
|
56
|
+
{ name: "limit", description: "Max entries to show (default: all)", type: "number" },
|
|
57
|
+
],
|
|
58
|
+
columns: [
|
|
59
|
+
{ key: "timestamp", header: "TIME", maxWidth: 24 },
|
|
60
|
+
{ key: "profile", header: "PROFILE", maxWidth: 20 },
|
|
61
|
+
{ key: "command", header: "COMMAND", maxWidth: 40 },
|
|
62
|
+
{ key: "status", header: "STATUS", maxWidth: 8 },
|
|
63
|
+
],
|
|
64
|
+
examples: [
|
|
65
|
+
{ description: "Show audit log for active profile", command: "casen settings audit-log" },
|
|
66
|
+
{
|
|
67
|
+
description: "Show audit log for a specific profile",
|
|
68
|
+
command: "casen settings audit-log --profile prod",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
async run(ctx) {
|
|
72
|
+
const profileFilter = ctx.flags.profile ?? getActiveName() ?? undefined;
|
|
73
|
+
const limit = ctx.flags.limit;
|
|
74
|
+
const entries = getAuditLog(profileFilter);
|
|
75
|
+
const display = limit !== undefined && limit > 0 ? entries.slice(-limit) : entries;
|
|
76
|
+
if (display.length === 0) {
|
|
77
|
+
ctx.output.info(profileFilter
|
|
78
|
+
? `No audit log entries for profile "${profileFilter}".`
|
|
79
|
+
: "No audit log entries.");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
ctx.output.printList({
|
|
83
|
+
items: display.map((e) => ({
|
|
84
|
+
timestamp: e.timestamp.replace("T", " ").slice(0, 19),
|
|
85
|
+
profile: profileFilter ?? "(all)",
|
|
86
|
+
command: `${e.group} ${e.command}${e.positional.length ? ` ${e.positional.join(" ")}` : ""}`,
|
|
87
|
+
status: e.status,
|
|
88
|
+
})),
|
|
89
|
+
}, [
|
|
90
|
+
{ key: "timestamp", header: "TIME", maxWidth: 20 },
|
|
91
|
+
{ key: "profile", header: "PROFILE", maxWidth: 20 },
|
|
92
|
+
{ key: "command", header: "COMMAND", maxWidth: 50 },
|
|
93
|
+
{ key: "status", header: "STATUS", maxWidth: 8 },
|
|
94
|
+
]);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "audit-log-clear",
|
|
99
|
+
aliases: ["log-clear"],
|
|
100
|
+
description: "Clear the audit log for the active (or specified) profile",
|
|
101
|
+
flags: [
|
|
102
|
+
{
|
|
103
|
+
name: "profile",
|
|
104
|
+
description: "Profile name to clear (defaults to active; use --all to clear all)",
|
|
105
|
+
type: "string",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "all",
|
|
109
|
+
description: "Clear audit log for all profiles",
|
|
110
|
+
type: "boolean",
|
|
111
|
+
default: false,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
examples: [
|
|
115
|
+
{
|
|
116
|
+
description: "Clear audit log for active profile",
|
|
117
|
+
command: "casen settings audit-log-clear",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
description: "Clear all audit logs",
|
|
121
|
+
command: "casen settings audit-log-clear --all",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
async run(ctx) {
|
|
125
|
+
const clearAll = ctx.flags.all === true;
|
|
126
|
+
if (clearAll) {
|
|
127
|
+
clearAuditLog();
|
|
128
|
+
ctx.output.ok("Cleared audit log for all profiles");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const profileName = ctx.flags.profile ?? getActiveName() ?? undefined;
|
|
132
|
+
clearAuditLog(profileName);
|
|
133
|
+
ctx.output.ok(profileName ? `Cleared audit log for profile "${profileName}"` : "Cleared audit log");
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// ─── Shared flag specs ────────────────────────────────────────────────────────
|
|
2
|
+
export const FILTER_FLAG = {
|
|
3
|
+
name: "filter",
|
|
4
|
+
short: "f",
|
|
5
|
+
description: "Filter as JSON object",
|
|
6
|
+
type: "string",
|
|
7
|
+
placeholder: "JSON",
|
|
8
|
+
json: true,
|
|
9
|
+
};
|
|
10
|
+
export const DATA_FLAG = {
|
|
11
|
+
name: "data",
|
|
12
|
+
short: "d",
|
|
13
|
+
description: "Request body as JSON",
|
|
14
|
+
type: "string",
|
|
15
|
+
required: true,
|
|
16
|
+
placeholder: "JSON",
|
|
17
|
+
json: true,
|
|
18
|
+
};
|
|
19
|
+
export const DATA_OPT_FLAG = {
|
|
20
|
+
name: "data",
|
|
21
|
+
short: "d",
|
|
22
|
+
description: "Request body as JSON",
|
|
23
|
+
type: "string",
|
|
24
|
+
placeholder: "JSON",
|
|
25
|
+
json: true,
|
|
26
|
+
};
|
|
27
|
+
export const LIMIT_FLAG = {
|
|
28
|
+
name: "limit",
|
|
29
|
+
short: "l",
|
|
30
|
+
description: "Maximum number of results",
|
|
31
|
+
type: "number",
|
|
32
|
+
default: 20,
|
|
33
|
+
presets: [5, 10, 20, 50, 100, 200, 500],
|
|
34
|
+
};
|
|
35
|
+
export const SORT_FLAG = {
|
|
36
|
+
name: "sort-by",
|
|
37
|
+
description: "Sort field",
|
|
38
|
+
type: "string",
|
|
39
|
+
placeholder: "FIELD",
|
|
40
|
+
};
|
|
41
|
+
export const SORT_ORDER_FLAG = {
|
|
42
|
+
name: "sort-order",
|
|
43
|
+
description: "Sort order",
|
|
44
|
+
type: "string",
|
|
45
|
+
default: "asc",
|
|
46
|
+
enum: ["asc", "desc"],
|
|
47
|
+
};
|
|
48
|
+
// ─── JSON helpers ─────────────────────────────────────────────────────────────
|
|
49
|
+
/**
|
|
50
|
+
* Parse a JSON flag value. Returns undefined for falsy values.
|
|
51
|
+
* Throws a descriptive error on invalid JSON.
|
|
52
|
+
*/
|
|
53
|
+
export function parseJson(value, flagName) {
|
|
54
|
+
if (!value)
|
|
55
|
+
return undefined;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(value);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
throw new Error(`Invalid JSON for --${flagName}: ${err instanceof Error ? err.message : String(err)}\n\nGot: ${value}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Build a search body from --filter, --limit, --sort-by, --sort-order flags. */
|
|
64
|
+
export function buildSearchBody(ctx) {
|
|
65
|
+
const filter = parseJson(ctx.flags.filter, "filter");
|
|
66
|
+
const limit = ctx.flags.limit;
|
|
67
|
+
const sortBy = ctx.flags["sort-by"];
|
|
68
|
+
const sortOrder = ctx.flags["sort-order"];
|
|
69
|
+
const body = {};
|
|
70
|
+
if (filter)
|
|
71
|
+
body.filter = filter;
|
|
72
|
+
if (limit !== undefined)
|
|
73
|
+
body.page = { limit };
|
|
74
|
+
if (sortBy)
|
|
75
|
+
body.sort = [{ field: sortBy, order: sortOrder ?? "asc" }];
|
|
76
|
+
return Object.keys(body).length > 0 ? body : undefined;
|
|
77
|
+
}
|
|
78
|
+
// ─── Command factories ────────────────────────────────────────────────────────
|
|
79
|
+
/** Factory for a "list" command that calls a search method. */
|
|
80
|
+
export function makeListCmd(opts) {
|
|
81
|
+
const filterFlag = opts.filterFields
|
|
82
|
+
? { ...FILTER_FLAG, fields: opts.filterFields }
|
|
83
|
+
: FILTER_FLAG;
|
|
84
|
+
return {
|
|
85
|
+
name: opts.name ?? "list",
|
|
86
|
+
aliases: opts.aliases,
|
|
87
|
+
description: opts.description,
|
|
88
|
+
columns: opts.columns,
|
|
89
|
+
flags: [filterFlag, LIMIT_FLAG, SORT_FLAG, SORT_ORDER_FLAG, ...(opts.extraFlags ?? [])],
|
|
90
|
+
examples: opts.examples,
|
|
91
|
+
async run(ctx) {
|
|
92
|
+
const client = await ctx.getClient();
|
|
93
|
+
const body = buildSearchBody(ctx);
|
|
94
|
+
const result = await opts.search(client, body);
|
|
95
|
+
ctx.output.printList(result, opts.columns);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Factory for a "get" command that takes a key positional argument. */
|
|
100
|
+
export function makeGetCmd(opts) {
|
|
101
|
+
return {
|
|
102
|
+
name: opts.name ?? "get",
|
|
103
|
+
aliases: opts.aliases,
|
|
104
|
+
description: opts.description,
|
|
105
|
+
args: [
|
|
106
|
+
{
|
|
107
|
+
name: opts.argName,
|
|
108
|
+
description: opts.argDesc ?? `${opts.argName} to retrieve`,
|
|
109
|
+
required: true,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
examples: opts.examples,
|
|
113
|
+
async run(ctx) {
|
|
114
|
+
const key = ctx.positional[0];
|
|
115
|
+
if (!key)
|
|
116
|
+
throw new Error(`Missing required argument: <${opts.argName}>`);
|
|
117
|
+
const client = await ctx.getClient();
|
|
118
|
+
const result = await opts.get(client, key);
|
|
119
|
+
ctx.output.printItem(result);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** Factory for a "delete" command that takes a key positional argument. */
|
|
124
|
+
export function makeDeleteCmd(opts) {
|
|
125
|
+
return {
|
|
126
|
+
name: opts.name ?? "delete",
|
|
127
|
+
aliases: opts.aliases,
|
|
128
|
+
description: opts.description,
|
|
129
|
+
args: [{ name: opts.argName, description: `${opts.argName} to delete`, required: true }],
|
|
130
|
+
flags: opts.extraFlags ? [DATA_OPT_FLAG, ...opts.extraFlags] : undefined,
|
|
131
|
+
examples: opts.examples,
|
|
132
|
+
async run(ctx) {
|
|
133
|
+
const key = ctx.positional[0];
|
|
134
|
+
if (!key)
|
|
135
|
+
throw new Error(`Missing required argument: <${opts.argName}>`);
|
|
136
|
+
const body = opts.extraFlags
|
|
137
|
+
? parseJson(ctx.flags.data, "data")
|
|
138
|
+
: undefined;
|
|
139
|
+
const client = await ctx.getClient();
|
|
140
|
+
await opts.delete(client, key, body);
|
|
141
|
+
const msg = opts.successMsg ? opts.successMsg(key) : `Deleted ${key}`;
|
|
142
|
+
ctx.output.ok(msg);
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Factory for a "create" command that posts a body. */
|
|
147
|
+
export function makeCreateCmd(opts) {
|
|
148
|
+
const dataFlag = opts.bodyFields ? { ...DATA_FLAG, fields: opts.bodyFields } : DATA_FLAG;
|
|
149
|
+
return {
|
|
150
|
+
name: opts.name ?? "create",
|
|
151
|
+
aliases: opts.aliases,
|
|
152
|
+
description: opts.description,
|
|
153
|
+
flags: [dataFlag, ...(opts.extraFlags ?? [])],
|
|
154
|
+
examples: opts.examples,
|
|
155
|
+
async run(ctx) {
|
|
156
|
+
const raw = ctx.flags.data;
|
|
157
|
+
const body = parseJson(raw, "data") ?? {};
|
|
158
|
+
const client = await ctx.getClient();
|
|
159
|
+
const result = await opts.create(client, body);
|
|
160
|
+
if (result !== undefined && result !== null) {
|
|
161
|
+
ctx.output.printItem(result);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
ctx.output.ok(opts.successMsg ?? "Created successfully.");
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Factory for an "update" command that patches a resource by key. */
|
|
170
|
+
export function makeUpdateCmd(opts) {
|
|
171
|
+
const dataFlag = opts.bodyFields ? { ...DATA_FLAG, fields: opts.bodyFields } : DATA_FLAG;
|
|
172
|
+
return {
|
|
173
|
+
name: opts.name ?? "update",
|
|
174
|
+
aliases: opts.aliases,
|
|
175
|
+
description: opts.description,
|
|
176
|
+
args: [{ name: opts.argName, description: `${opts.argName} to update`, required: true }],
|
|
177
|
+
flags: [dataFlag, ...(opts.extraFlags ?? [])],
|
|
178
|
+
examples: opts.examples,
|
|
179
|
+
async run(ctx) {
|
|
180
|
+
const key = ctx.positional[0];
|
|
181
|
+
if (!key)
|
|
182
|
+
throw new Error(`Missing required argument: <${opts.argName}>`);
|
|
183
|
+
const raw = ctx.flags.data;
|
|
184
|
+
const body = parseJson(raw, "data") ?? {};
|
|
185
|
+
const client = await ctx.getClient();
|
|
186
|
+
const result = await opts.update(client, key, body);
|
|
187
|
+
if (result !== undefined && result !== null) {
|
|
188
|
+
ctx.output.printItem(result);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
ctx.output.ok(`Updated ${key}.`);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple job worker that polls for jobs of a given type and auto-completes
|
|
3
|
+
* them with a configurable variables payload. Intended for local development
|
|
4
|
+
* and learning — lets users try out the job-worker concept without writing code.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. POST /jobs/activation — activate up to N jobs (long-polling)
|
|
8
|
+
* 2. POST /jobs/{key}/completion — complete each job with sample variables
|
|
9
|
+
* 3. Repeat until Ctrl+C
|
|
10
|
+
*/
|
|
11
|
+
export const workerCmd = {
|
|
12
|
+
name: "worker",
|
|
13
|
+
description: "Run a simple job worker that auto-completes jobs of a given type",
|
|
14
|
+
args: [
|
|
15
|
+
{
|
|
16
|
+
name: "type",
|
|
17
|
+
description: "Job type to subscribe to (matches the task definition type in BPMN)",
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
flags: [
|
|
22
|
+
{
|
|
23
|
+
name: "variables",
|
|
24
|
+
short: "v",
|
|
25
|
+
description: "Variables to return when completing each job (JSON object)",
|
|
26
|
+
type: "string",
|
|
27
|
+
placeholder: "JSON",
|
|
28
|
+
default: '{"result":"sample-value"}',
|
|
29
|
+
json: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "timeout",
|
|
33
|
+
short: "t",
|
|
34
|
+
description: "Job activation lock timeout in milliseconds",
|
|
35
|
+
type: "number",
|
|
36
|
+
default: 30000,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "max-jobs",
|
|
40
|
+
short: "m",
|
|
41
|
+
description: "Maximum number of jobs to activate per poll",
|
|
42
|
+
type: "number",
|
|
43
|
+
default: 32,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
examples: [
|
|
47
|
+
{
|
|
48
|
+
description: "Subscribe to jobs of type 'payment-service'",
|
|
49
|
+
command: "casen job worker payment-service",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
description: "Return custom variables on completion",
|
|
53
|
+
command: 'casen job worker payment-service --variables \'{"status":"ok","amount":100}\'',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
async run(ctx) {
|
|
57
|
+
const jobType = ctx.positional[0];
|
|
58
|
+
if (!jobType)
|
|
59
|
+
throw new Error("Missing required argument: <type>");
|
|
60
|
+
const timeout = ctx.flags.timeout ?? 30000;
|
|
61
|
+
const maxJobs = ctx.flags["max-jobs"] ?? 32;
|
|
62
|
+
let variables = { result: "sample-value" };
|
|
63
|
+
const rawVars = ctx.flags.variables;
|
|
64
|
+
if (rawVars && typeof rawVars === "string") {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(rawVars);
|
|
67
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
68
|
+
variables = parsed;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new Error("--variables must be a JSON object");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
throw new Error(`Invalid --variables JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const client = await ctx.getClient();
|
|
79
|
+
ctx.output.info(`Starting worker for job type "${jobType}"`);
|
|
80
|
+
ctx.output.info(`Completing jobs with: ${JSON.stringify(variables)}`);
|
|
81
|
+
ctx.output.info("Press Ctrl+C to stop\n");
|
|
82
|
+
let running = true;
|
|
83
|
+
let completed = 0;
|
|
84
|
+
process.once("SIGINT", () => {
|
|
85
|
+
running = false;
|
|
86
|
+
});
|
|
87
|
+
while (running) {
|
|
88
|
+
let result;
|
|
89
|
+
try {
|
|
90
|
+
result = (await client.job.activateJobs({
|
|
91
|
+
type: jobType,
|
|
92
|
+
worker: "casen-worker",
|
|
93
|
+
timeout,
|
|
94
|
+
maxJobsToActivate: maxJobs,
|
|
95
|
+
requestTimeout: 20000, // 20 s long poll
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (!running)
|
|
100
|
+
break;
|
|
101
|
+
// On 503 / backpressure, wait briefly before retrying
|
|
102
|
+
ctx.output.info(`Poll error: ${err instanceof Error ? err.message : String(err)} — retrying in 5 s`);
|
|
103
|
+
await delay(5000);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const jobs = result?.jobs ?? [];
|
|
107
|
+
for (const job of jobs) {
|
|
108
|
+
if (!running)
|
|
109
|
+
break;
|
|
110
|
+
ctx.output.info(`Activated job ${job.jobKey} process=${job.processDefinitionId} element=${job.elementId} instance=${job.processInstanceKey}`);
|
|
111
|
+
if (Object.keys(job.variables ?? {}).length > 0) {
|
|
112
|
+
ctx.output.info(` Input variables: ${JSON.stringify(job.variables)}`);
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
await client.job.completeJob(job.jobKey, { variables });
|
|
116
|
+
completed++;
|
|
117
|
+
ctx.output.ok(`Completed job ${job.jobKey} (total: ${completed})`);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
ctx.output.info(`Failed to complete job ${job.jobKey}: ${err instanceof Error ? err.message : String(err)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// When no jobs were returned, the long-poll already waited; loop immediately.
|
|
124
|
+
// When jobs were found, yield the event loop briefly before the next poll.
|
|
125
|
+
if (jobs.length > 0) {
|
|
126
|
+
await delay(100);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
ctx.output.info(`\nWorker stopped. Completed ${completed} job(s).`);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
function delay(ms) {
|
|
133
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ─── Runtime completion ────────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Return newline-separated completion suggestions for the given cursor
|
|
4
|
+
* position and word list. Called when the binary runs with --complete.
|
|
5
|
+
*
|
|
6
|
+
* Protocol: casen --complete <cursorWordIndex> -- <words...>
|
|
7
|
+
*/
|
|
8
|
+
export function getRuntimeCompletions(groups, cursorIdx, words) {
|
|
9
|
+
// words[0] is the binary itself; strip it
|
|
10
|
+
const tokens = words.slice(1);
|
|
11
|
+
const pos = cursorIdx - 1; // adjust for stripped binary
|
|
12
|
+
const partial = tokens[pos] ?? "";
|
|
13
|
+
// Position 0: completing the resource group name
|
|
14
|
+
if (pos === 0) {
|
|
15
|
+
const names = groups.flatMap((g) => [g.name, ...(g.aliases ?? [])]);
|
|
16
|
+
return names.filter((n) => n.startsWith(partial));
|
|
17
|
+
}
|
|
18
|
+
// Find which group is selected
|
|
19
|
+
const groupToken = tokens[0] ?? "";
|
|
20
|
+
const group = groups.find((g) => g.name === groupToken || g.aliases?.includes(groupToken));
|
|
21
|
+
if (!group)
|
|
22
|
+
return [];
|
|
23
|
+
// Position 1: completing the command name
|
|
24
|
+
if (pos === 1) {
|
|
25
|
+
const names = group.commands.flatMap((c) => [c.name, ...(c.aliases ?? [])]);
|
|
26
|
+
return names.filter((n) => n.startsWith(partial));
|
|
27
|
+
}
|
|
28
|
+
// Position 2+: completing flags for the active command
|
|
29
|
+
const cmdToken = tokens[1] ?? "";
|
|
30
|
+
const cmd = group.commands.find((c) => c.name === cmdToken || c.aliases?.includes(cmdToken));
|
|
31
|
+
if (!cmd)
|
|
32
|
+
return [];
|
|
33
|
+
// Suggest flags
|
|
34
|
+
if (partial.startsWith("--") || partial === "") {
|
|
35
|
+
const flagNames = (cmd.flags ?? []).map((f) => `--${f.name}`);
|
|
36
|
+
const globalFlags = ["--profile", "--output", "--no-color", "--debug", "--help"];
|
|
37
|
+
return [...flagNames, ...globalFlags].filter((f) => f.startsWith(partial));
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
// ─── Shell scripts ────────────────────────────────────────────────────────────
|
|
42
|
+
export function getBashScript() {
|
|
43
|
+
return `# bash completion for casen
|
|
44
|
+
# Add to ~/.bash_completion or source in ~/.bashrc:
|
|
45
|
+
# eval "$(casen completion bash)"
|
|
46
|
+
|
|
47
|
+
_casen_complete() {
|
|
48
|
+
local cur_word
|
|
49
|
+
cur_word="\${COMP_WORDS[COMP_CWORD]}"
|
|
50
|
+
local completions
|
|
51
|
+
completions=$(casen --complete $COMP_CWORD -- "\${COMP_WORDS[@]}" 2>/dev/null)
|
|
52
|
+
COMPREPLY=($(compgen -W "$completions" -- "$cur_word"))
|
|
53
|
+
}
|
|
54
|
+
complete -F _casen_complete casen
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
export function getZshScript() {
|
|
58
|
+
return `#compdef casen
|
|
59
|
+
# zsh completion for casen
|
|
60
|
+
# Add to a directory in your \$fpath, e.g.:
|
|
61
|
+
# mkdir -p ~/.zfunc && casen completion zsh > ~/.zfunc/_casen
|
|
62
|
+
# Then ensure the directory is in your fpath before compinit:
|
|
63
|
+
# fpath=(~/.zfunc $fpath)
|
|
64
|
+
# autoload -Uz compinit && compinit
|
|
65
|
+
|
|
66
|
+
_casen() {
|
|
67
|
+
local -a completions
|
|
68
|
+
IFS=$'\\n' completions=( $(casen --complete $((CURRENT - 1)) -- "\${words[@]}" 2>/dev/null) )
|
|
69
|
+
compadd -a completions
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_casen
|
|
73
|
+
`;
|
|
74
|
+
}
|
|
75
|
+
export function getFishScript() {
|
|
76
|
+
return `# fish completion for casen
|
|
77
|
+
# Copy to ~/.config/fish/completions/casen.fish or run:
|
|
78
|
+
# casen completion fish > ~/.config/fish/completions/casen.fish
|
|
79
|
+
|
|
80
|
+
function __casen_complete
|
|
81
|
+
set -l cmd (commandline -opc)
|
|
82
|
+
set -l pos (count $cmd)
|
|
83
|
+
casen --complete $pos -- $cmd 2>/dev/null
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
complete -c casen -f -a '(__casen_complete)'
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=completion.js.map
|