@formigio/fazemos-cli 0.10.38 → 0.10.40
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/index.js +101 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2383,8 +2383,20 @@ ws
|
|
|
2383
2383
|
.argument('<id>', 'Worksheet ID')
|
|
2384
2384
|
.action(async (id) => {
|
|
2385
2385
|
try {
|
|
2386
|
-
|
|
2386
|
+
// G4 (AC15): fetch enriched actions in parallel so we can display
|
|
2387
|
+
// linked_pipeline status chips next to bound actions.
|
|
2388
|
+
const [data, actionsResult] = await Promise.all([
|
|
2389
|
+
api('GET', `/api/worksheets/${id}`),
|
|
2390
|
+
api('GET', `/api/worksheets/${id}/actions`).catch(() => null),
|
|
2391
|
+
]);
|
|
2387
2392
|
const w = data.worksheet;
|
|
2393
|
+
// Build a lookup map from action id → linked_pipeline (from the enriched endpoint)
|
|
2394
|
+
const actionPipelineMap = new Map();
|
|
2395
|
+
if (actionsResult?.actions) {
|
|
2396
|
+
for (const a of actionsResult.actions) {
|
|
2397
|
+
actionPipelineMap.set(a.id, a.linked_pipeline ?? null);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2388
2400
|
console.log(chalk.cyan(w.name));
|
|
2389
2401
|
console.log(` ID: ${w.id}`);
|
|
2390
2402
|
console.log(` Purpose: ${w.purpose || '(none)'}`);
|
|
@@ -2414,7 +2426,16 @@ ws
|
|
|
2414
2426
|
console.log(chalk.cyan(`\n Actions (${data.actions.length}):`));
|
|
2415
2427
|
for (const a of data.actions) {
|
|
2416
2428
|
const progress = a.target_value ? ` ${a.current_value ?? 0}/${a.target_value}` : '';
|
|
2417
|
-
|
|
2429
|
+
// G4 (AC15): show pipeline status chip when action is bound to a pipeline
|
|
2430
|
+
const linkedPipeline = actionPipelineMap.get(a.id);
|
|
2431
|
+
let pipelineChip = '';
|
|
2432
|
+
if (linkedPipeline) {
|
|
2433
|
+
const chipColor = linkedPipeline.status === 'completed' ? chalk.green :
|
|
2434
|
+
linkedPipeline.status === 'failed' ? chalk.red :
|
|
2435
|
+
chalk.yellow;
|
|
2436
|
+
pipelineChip = ` ${chipColor(`[${linkedPipeline.status}]`)}`;
|
|
2437
|
+
}
|
|
2438
|
+
console.log(` ${chalk.cyan(a.description)}${progress}${pipelineChip} — ${a.member_name || 'unassigned'} — ${a.id}`);
|
|
2418
2439
|
}
|
|
2419
2440
|
}
|
|
2420
2441
|
if (data.commitments?.length) {
|
|
@@ -4808,17 +4829,29 @@ pipelines
|
|
|
4808
4829
|
.argument('<templateId>', 'Template ID')
|
|
4809
4830
|
.requiredOption('-n, --name <name>', 'Instance name')
|
|
4810
4831
|
.option('--params <json>', 'Parameters as JSON string')
|
|
4832
|
+
.option('--action <action-id>', 'Bind to a worksheet action at creation time (UUID)')
|
|
4811
4833
|
.action(async (templateId, opts) => {
|
|
4812
4834
|
try {
|
|
4813
4835
|
const body = { templateId, name: opts.name };
|
|
4814
4836
|
if (opts.params)
|
|
4815
4837
|
body.parameters = parseJson(opts.params, '--params');
|
|
4838
|
+
// G4: optionally bind to a worksheet action at creation time (AC12)
|
|
4839
|
+
if (opts.action)
|
|
4840
|
+
body.worksheetActionId = opts.action;
|
|
4816
4841
|
const data = await api('POST', '/api/pipeline-instances', body);
|
|
4817
4842
|
const inst = data.instance;
|
|
4818
4843
|
console.log(chalk.green(`Created: ${inst.name}`));
|
|
4819
4844
|
console.log(` ID: ${inst.id}`);
|
|
4845
|
+
if (inst.worksheet_action_id) {
|
|
4846
|
+
console.log(` Bound Action: ${inst.worksheet_action_id}`);
|
|
4847
|
+
}
|
|
4820
4848
|
}
|
|
4821
4849
|
catch (err) {
|
|
4850
|
+
// G4: org-isolation guard fires when the action belongs to a different org (AC11)
|
|
4851
|
+
if (err instanceof ApiError && err.code === 'BINDING_ORG_ISOLATION_FAILED') {
|
|
4852
|
+
console.error(chalk.red('Binding failed: the action belongs to a different org than this pipeline.'));
|
|
4853
|
+
process.exit(1);
|
|
4854
|
+
}
|
|
4822
4855
|
// F16 — Role-aware Connection-unavailable error (tech spec §5.14).
|
|
4823
4856
|
// The pipeline-run path returns a structured payload when the
|
|
4824
4857
|
// Project's Connection is missing/revoked/suspended/uninstalled.
|
|
@@ -4855,6 +4888,50 @@ pipelines
|
|
|
4855
4888
|
process.exit(1);
|
|
4856
4889
|
}
|
|
4857
4890
|
});
|
|
4891
|
+
// G4 — Bind an existing pipeline instance to a worksheet action (AC13)
|
|
4892
|
+
pipelines
|
|
4893
|
+
.command('bind')
|
|
4894
|
+
.description('Bind an existing pipeline instance to a worksheet action')
|
|
4895
|
+
.argument('<instance-id>', 'Pipeline instance ID')
|
|
4896
|
+
.requiredOption('--action <action-id>', 'Worksheet action UUID to bind')
|
|
4897
|
+
.action(async (instanceId, opts) => {
|
|
4898
|
+
try {
|
|
4899
|
+
const data = await api('PATCH', `/api/pipeline-instances/${instanceId}`, {
|
|
4900
|
+
worksheetActionId: opts.action,
|
|
4901
|
+
});
|
|
4902
|
+
const inst = data.instance;
|
|
4903
|
+
console.log(chalk.green(`Bound: ${inst.name}`));
|
|
4904
|
+
console.log(` Pipeline: ${inst.id}`);
|
|
4905
|
+
console.log(` Action: ${inst.worksheet_action_id}`);
|
|
4906
|
+
}
|
|
4907
|
+
catch (err) {
|
|
4908
|
+
if (err instanceof ApiError && err.code === 'BINDING_ORG_ISOLATION_FAILED') {
|
|
4909
|
+
console.error(chalk.red('Binding failed: the action belongs to a different org than this pipeline.'));
|
|
4910
|
+
process.exit(1);
|
|
4911
|
+
}
|
|
4912
|
+
console.error(chalk.red(err.message));
|
|
4913
|
+
process.exit(1);
|
|
4914
|
+
}
|
|
4915
|
+
});
|
|
4916
|
+
// G4 — Remove binding between a pipeline instance and a worksheet action (AC14)
|
|
4917
|
+
pipelines
|
|
4918
|
+
.command('unbind')
|
|
4919
|
+
.description('Remove binding between a pipeline instance and a worksheet action')
|
|
4920
|
+
.argument('<instance-id>', 'Pipeline instance ID')
|
|
4921
|
+
.action(async (instanceId) => {
|
|
4922
|
+
try {
|
|
4923
|
+
const data = await api('PATCH', `/api/pipeline-instances/${instanceId}`, {
|
|
4924
|
+
worksheetActionId: null,
|
|
4925
|
+
});
|
|
4926
|
+
const inst = data.instance;
|
|
4927
|
+
console.log(chalk.green(`Unbound: ${inst.name}`));
|
|
4928
|
+
console.log(` Pipeline: ${inst.id}`);
|
|
4929
|
+
}
|
|
4930
|
+
catch (err) {
|
|
4931
|
+
console.error(chalk.red(err.message));
|
|
4932
|
+
process.exit(1);
|
|
4933
|
+
}
|
|
4934
|
+
});
|
|
4858
4935
|
// I30 — `pl force-cancel` removed. Replaced by the generalised
|
|
4859
4936
|
// `pl force-transition pipeline <id> --to cancelled` admin endpoint, which
|
|
4860
4937
|
// covers the same emergency cancel path plus 12 more entity/state combos
|
|
@@ -5643,9 +5720,15 @@ program
|
|
|
5643
5720
|
maxBudgetUsd: opts.budget || agentConfig.maxBudgetUsd || 10,
|
|
5644
5721
|
maxTurns: agentConfig.maxTurns || 100,
|
|
5645
5722
|
timeoutMs: 1800000,
|
|
5723
|
+
// [F22-FU-CLI-CLONE-URL] Send only { name, branch } — NOT a hardcoded
|
|
5724
|
+
// clone url. The API (POST /api/executions) resolves the real clone URL
|
|
5725
|
+
// server-side via buildProjectCloneUrl(sourceProjectId, name) from the
|
|
5726
|
+
// source project's bound VCS Connection, using only the repo `name` and
|
|
5727
|
+
// ignoring any CLI-supplied `url`. Hardcoding a host/org here would be a
|
|
5728
|
+
// de-branding leak (github.com/formigio/...) for external/OSS consumers,
|
|
5729
|
+
// so the CLI must not carry it.
|
|
5646
5730
|
repos: repoNames.map((r) => ({
|
|
5647
5731
|
name: r,
|
|
5648
|
-
url: `https://github.com/formigio/${r}.git`,
|
|
5649
5732
|
branch: 'main',
|
|
5650
5733
|
})),
|
|
5651
5734
|
};
|
|
@@ -5759,10 +5842,12 @@ program
|
|
|
5759
5842
|
const executions = program.command('executions').alias('ex').description('Execution commands');
|
|
5760
5843
|
executions
|
|
5761
5844
|
.command('list')
|
|
5762
|
-
.description('List executions')
|
|
5845
|
+
.description('List executions in the active project (or all projects with --all-projects)')
|
|
5763
5846
|
.option('-s, --status <status>', 'Filter by status (pending, running, completed, failed)')
|
|
5764
5847
|
.option('-a, --agent <name>', 'Filter by agent')
|
|
5765
5848
|
.option('--source-type <type>', 'Filter by source type')
|
|
5849
|
+
.option('--project <slug>', 'Override active project for this call')
|
|
5850
|
+
.option('--all-projects', 'List executions across every project in the active org', false)
|
|
5766
5851
|
.action(async (opts) => {
|
|
5767
5852
|
try {
|
|
5768
5853
|
const params = [];
|
|
@@ -5773,7 +5858,7 @@ executions
|
|
|
5773
5858
|
if (opts.sourceType)
|
|
5774
5859
|
params.push(`source_type=${opts.sourceType}`);
|
|
5775
5860
|
const qs = params.length ? `?${params.join('&')}` : '';
|
|
5776
|
-
const data = await api('GET', `/api/executions${qs}
|
|
5861
|
+
const data = await api('GET', `/api/executions${qs}`, undefined, projectOpts(opts));
|
|
5777
5862
|
if (!data.executions?.length) {
|
|
5778
5863
|
console.log(chalk.yellow('No executions'));
|
|
5779
5864
|
return;
|
|
@@ -5784,12 +5869,20 @@ executions
|
|
|
5784
5869
|
: e.status === 'failed' ? chalk.red('✗')
|
|
5785
5870
|
: '○';
|
|
5786
5871
|
const cost = e.cost_usd ? ` $${e.cost_usd}` : '';
|
|
5787
|
-
|
|
5872
|
+
// AUTON-PORTFOLIO-VIEW — in the org-wide fleet view (--all-projects),
|
|
5873
|
+
// surface which project each execution belongs to so the list is
|
|
5874
|
+
// legible across projects. Degrades gracefully to project_id (the
|
|
5875
|
+
// executions list payload today includes project_id but not slug/name)
|
|
5876
|
+
// and is omitted entirely in the default single-project view where a
|
|
5877
|
+
// project column would be redundant.
|
|
5878
|
+
const projectCell = opts.allProjects
|
|
5879
|
+
? chalk.gray(` [${e.project_slug || e.project_name || e.project_id || '—'}]`)
|
|
5880
|
+
: '';
|
|
5881
|
+
console.log(` ${icon} ${e.agent_name} — ${e.source_type} (${e.status})${cost}${projectCell} — ${e.id}`);
|
|
5788
5882
|
}
|
|
5789
5883
|
}
|
|
5790
5884
|
catch (err) {
|
|
5791
|
-
|
|
5792
|
-
process.exit(1);
|
|
5885
|
+
handleScopedError(err);
|
|
5793
5886
|
}
|
|
5794
5887
|
});
|
|
5795
5888
|
executions
|