@formigio/fazemos-cli 0.10.8 → 0.10.10
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/dist/api.d.ts +8 -0
- package/dist/api.js +8 -0
- package/dist/api.js.map +1 -1
- package/dist/index.js +107 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -29,6 +29,14 @@ export interface ApiOptions {
|
|
|
29
29
|
projectSlug?: string;
|
|
30
30
|
allProjects?: boolean;
|
|
31
31
|
noProjectHeader?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extra request headers merged on top of the standard auth/org/project
|
|
34
|
+
* headers. Used by callers that need optimistic-concurrency or other
|
|
35
|
+
* out-of-band headers (e.g., I30 force-transition's `If-Match: <n>` opt-in).
|
|
36
|
+
* Caller-supplied keys win on conflict — this is intentional so a caller
|
|
37
|
+
* can override `Content-Type` if needed.
|
|
38
|
+
*/
|
|
39
|
+
headers?: Record<string, string>;
|
|
32
40
|
}
|
|
33
41
|
/**
|
|
34
42
|
* Resolve the project id to send as X-Fazemos-Project-Id on a scoped call.
|
package/dist/api.js
CHANGED
|
@@ -119,6 +119,14 @@ export async function api(method, path, body, opts = {}) {
|
|
|
119
119
|
headers['X-Fazemos-Project-Id'] = projectId;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
// I30 — caller-supplied extra headers (e.g., If-Match for opt-in optimistic
|
|
123
|
+
// concurrency on force-transition). Applied last so callers can override
|
|
124
|
+
// anything we set above on purpose.
|
|
125
|
+
if (opts.headers) {
|
|
126
|
+
for (const [k, v] of Object.entries(opts.headers)) {
|
|
127
|
+
headers[k] = v;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
122
130
|
// Build URL with optional ?view=all append. We don't try to be clever
|
|
123
131
|
// about merging with existing query strings here — callers that use
|
|
124
132
|
// --all-projects know they're on GET list endpoints with no other query.
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,IAAI,CAAU;IACd,IAAI,CAAW;IACf,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa,EAAE,IAAc;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,IAAI,CAAU;IACd,IAAI,CAAW;IACf,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa,EAAE,IAAc;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AA4BD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,YAAqB;IAChE,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,OAAO,GAAG,iBAAiB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,mEAAmE;YACnE,kEAAkE;YAClE,oCAAoC;YACpC,MAAM,kBAAkB,EAAE,CAAC;YAC3B,OAAO,GAAG,iBAAiB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,QAAQ,CAChB,oBAAoB,YAAY,EAAE,EAClC,GAAG,EACH,iBAAiB,CAClB,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC,EAAE,CAAC;IACpB,CAAC;IAED,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAmB,CAAC;QAClG,IAAI,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;YACjD,cAAc,CAAC,EAAE,CAAC,CAAC;YACnB,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;QAClE,gEAAgE;QAChE,iBAAiB;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,MAAc,EACd,IAAY,EACZ,IAAc,EACd,OAAmB,EAAE;IAErB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,mCAAmC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,MAAM,cAAc,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;KACnC,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;IAC/C,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;IAC9B,CAAC;IAED,yEAAyE;IACzE,2EAA2E;IAC3E,yEAAyE;IACzE,mBAAmB;IACnB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACjE,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,sBAAsB,CAAC,GAAG,SAAS,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,oCAAoC;IACpC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAClD,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,oEAAoE;IACpE,yEAAyE;IACzE,qEAAqE;IACrE,oDAAoD;IACpD,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;IACtD,CAAC;IAED,MAAM,OAAO,GAAgB;QAC3B,MAAM;QACN,OAAO;KACR,CAAC;IAEF,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEnC,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,GAAG,GACP,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YACvC,CAAC,CAAE,IAA0C;YAC7C,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,EAAE,KAAK,IAAI,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QAEpD,+DAA+D;QAC/D,8DAA8D;QAC9D,IAAI,IAAI,KAAK,yBAAyB,EAAE,CAAC;YACvC,MAAM,IAAI,QAAQ,CAChB,8BAA8B,EAC9B,QAAQ,CAAC,MAAM,EACf,IAAI,EACJ,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,gBAAgB,EAAE,CAAC;AACrB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4022,18 +4022,114 @@ pipelines
|
|
|
4022
4022
|
process.exit(1);
|
|
4023
4023
|
}
|
|
4024
4024
|
});
|
|
4025
|
+
// I30 — `pl force-cancel` removed. Replaced by the generalised
|
|
4026
|
+
// `pl force-transition pipeline <id> --to cancelled` admin endpoint, which
|
|
4027
|
+
// covers the same emergency cancel path plus 12 more entity/state combos
|
|
4028
|
+
// across pipelines, steps, and executions. No shim: callers update or 404.
|
|
4025
4029
|
pipelines
|
|
4026
|
-
.command('force-
|
|
4027
|
-
.description('Force
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
.
|
|
4031
|
-
|
|
4032
|
-
|
|
4030
|
+
.command('force-transition')
|
|
4031
|
+
.description('Force an entity (pipeline, step, or execution) into a target state. Admin / org owner / project member only. ' +
|
|
4032
|
+
'Bypasses normal state transition rules and fires the appropriate subscriber chain (audit log, ECS stop, ' +
|
|
4033
|
+
'agent reset, token revoke, etc.). Use only when the normal lifecycle is stuck.')
|
|
4034
|
+
.argument('<entity-type>', 'Entity type: pipeline | step | execution')
|
|
4035
|
+
.argument('<id>', 'Entity ID (pipeline_instance / step_instance / execution UUID)')
|
|
4036
|
+
.requiredOption('--to <state>', 'Target state (e.g., cancelled, failed, completed, in_progress, ...)')
|
|
4037
|
+
.option('-r, --reason <reason>', 'Free-form audit reason (recorded in audit_log)')
|
|
4038
|
+
.option('--output-data-file <path>', 'Path to a JSON file with output_data. Required when forcing a step with a structured output schema to "completed".')
|
|
4039
|
+
.option('--expect-version <n>', 'Optimistic-concurrency guard: send If-Match: "<n>" so the API rejects the call (409) ' +
|
|
4040
|
+
'if the current entity version differs. Omit to bypass version checking (default).')
|
|
4041
|
+
.option('--json', 'Print the raw API response as JSON (machine-readable)')
|
|
4042
|
+
.action(async (entityType, id, opts) => {
|
|
4043
|
+
try {
|
|
4044
|
+
const valid = ['pipeline', 'step', 'execution'];
|
|
4045
|
+
if (!valid.includes(entityType)) {
|
|
4046
|
+
console.error(chalk.red(`Invalid entity-type "${entityType}". Must be one of: ${valid.join(', ')}`));
|
|
4047
|
+
process.exit(1);
|
|
4048
|
+
}
|
|
4049
|
+
if (!opts.to || typeof opts.to !== 'string' || !opts.to.trim()) {
|
|
4050
|
+
console.error(chalk.red('--to <state> is required'));
|
|
4051
|
+
process.exit(1);
|
|
4052
|
+
}
|
|
4053
|
+
// Body field names match the API contract verbatim:
|
|
4054
|
+
// target_state — required
|
|
4055
|
+
// reason — optional, free-form audit string
|
|
4056
|
+
// output_data — required for step→completed when the step has an
|
|
4057
|
+
// output schema; carrier rejects with 400 OUTPUT_DATA_*
|
|
4058
|
+
// otherwise.
|
|
4059
|
+
const body = { target_state: opts.to };
|
|
4033
4060
|
if (opts.reason)
|
|
4034
4061
|
body.reason = opts.reason;
|
|
4035
|
-
|
|
4036
|
-
|
|
4062
|
+
if (opts.outputDataFile) {
|
|
4063
|
+
if (entityType !== 'step') {
|
|
4064
|
+
console.error(chalk.red('--output-data-file is only valid when entity-type is "step"'));
|
|
4065
|
+
process.exit(1);
|
|
4066
|
+
}
|
|
4067
|
+
let raw;
|
|
4068
|
+
try {
|
|
4069
|
+
raw = readFileSync(resolve(opts.outputDataFile), 'utf-8');
|
|
4070
|
+
}
|
|
4071
|
+
catch (err) {
|
|
4072
|
+
console.error(chalk.red(`Cannot read --output-data-file "${opts.outputDataFile}": ${err.message}`));
|
|
4073
|
+
process.exit(1);
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4076
|
+
let parsed;
|
|
4077
|
+
try {
|
|
4078
|
+
parsed = JSON.parse(raw);
|
|
4079
|
+
}
|
|
4080
|
+
catch (err) {
|
|
4081
|
+
console.error(chalk.red(`Invalid JSON in --output-data-file "${opts.outputDataFile}": ${err.message}`));
|
|
4082
|
+
process.exit(1);
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
body.output_data = parsed;
|
|
4086
|
+
}
|
|
4087
|
+
// --expect-version <n> opts the caller in to optimistic concurrency.
|
|
4088
|
+
// Default is bypass (no header) per the I30 spec Decision #4.
|
|
4089
|
+
const apiOpts = {};
|
|
4090
|
+
if (opts.expectVersion !== undefined) {
|
|
4091
|
+
const n = Number(opts.expectVersion);
|
|
4092
|
+
if (!Number.isInteger(n)) {
|
|
4093
|
+
console.error(chalk.red(`--expect-version must be an integer; got "${opts.expectVersion}"`));
|
|
4094
|
+
process.exit(1);
|
|
4095
|
+
}
|
|
4096
|
+
apiOpts.headers = { 'If-Match': String(n) };
|
|
4097
|
+
}
|
|
4098
|
+
const path = `/api/admin/force-transition/${entityType}/${id}`;
|
|
4099
|
+
const data = (await api('POST', path, body, apiOpts));
|
|
4100
|
+
if (opts.json) {
|
|
4101
|
+
console.log(JSON.stringify(data, null, 2));
|
|
4102
|
+
return;
|
|
4103
|
+
}
|
|
4104
|
+
// Human-readable summary. Per-entity payloads diverge (instance / step /
|
|
4105
|
+
// execution) so we surface whichever is present and always echo the
|
|
4106
|
+
// forensic fields (audit_log_id, subscribers_fired, subscriber_failures,
|
|
4107
|
+
// orphan_executions_cancelled).
|
|
4108
|
+
const entityPayload = data?.instance || data?.step || data?.execution || null;
|
|
4109
|
+
const label = entityPayload?.name || entityPayload?.step_name || entityPayload?.id || id;
|
|
4110
|
+
console.log(chalk.green(`Force-transitioned ${entityType} ${label} → ${opts.to}`));
|
|
4111
|
+
if (data?.audit_log_id) {
|
|
4112
|
+
console.log(chalk.gray(` audit_log_id: ${data.audit_log_id}`));
|
|
4113
|
+
}
|
|
4114
|
+
const fired = Array.isArray(data?.subscribers_fired) ? data.subscribers_fired : [];
|
|
4115
|
+
if (fired.length) {
|
|
4116
|
+
console.log(chalk.gray(` subscribers_fired: ${fired.join(', ')}`));
|
|
4117
|
+
}
|
|
4118
|
+
const failures = Array.isArray(data?.subscriber_failures)
|
|
4119
|
+
? data.subscriber_failures
|
|
4120
|
+
: [];
|
|
4121
|
+
if (failures.length) {
|
|
4122
|
+
console.log(chalk.yellow(` subscriber_failures: ${failures.length}`));
|
|
4123
|
+
for (const f of failures) {
|
|
4124
|
+
console.log(chalk.yellow(` - ${f.id || f.subscriber || '?'}: ${f.error}`));
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
const orphans = Array.isArray(data?.orphan_executions_cancelled)
|
|
4128
|
+
? data.orphan_executions_cancelled
|
|
4129
|
+
: [];
|
|
4130
|
+
if (orphans.length) {
|
|
4131
|
+
console.log(chalk.gray(` orphan_executions_cancelled: ${orphans.join(', ')}`));
|
|
4132
|
+
}
|
|
4037
4133
|
}
|
|
4038
4134
|
catch (err) {
|
|
4039
4135
|
console.error(chalk.red(err.message));
|
|
@@ -4058,7 +4154,7 @@ pipelines
|
|
|
4058
4154
|
});
|
|
4059
4155
|
pipelines
|
|
4060
4156
|
.command('set-status')
|
|
4061
|
-
.description('Update instance status. Allowed: paused, cancelled. Use "pl force-
|
|
4157
|
+
.description('Update instance status. Allowed: paused, cancelled. Use "pl force-transition pipeline <id> --to cancelled" for stuck instances.')
|
|
4062
4158
|
.argument('<id>', 'Instance ID')
|
|
4063
4159
|
.requiredOption('--status <status>', 'New status (paused or cancelled)')
|
|
4064
4160
|
.action(async (id, opts) => {
|
|
@@ -4066,7 +4162,7 @@ pipelines
|
|
|
4066
4162
|
const valid = ['paused', 'cancelled'];
|
|
4067
4163
|
if (!valid.includes(opts.status)) {
|
|
4068
4164
|
console.error(chalk.red(`Invalid status. Must be one of: ${valid.join(', ')}`));
|
|
4069
|
-
console.error(chalk.yellow('Tip: Use "pl force-
|
|
4165
|
+
console.error(chalk.yellow('Tip: Use "pl force-transition pipeline <id> --to cancelled" to cancel stuck pipelines from any state.'));
|
|
4070
4166
|
process.exit(1);
|
|
4071
4167
|
}
|
|
4072
4168
|
const data = await api('PATCH', `/api/pipeline-instances/${id}/status`, { status: opts.status });
|