@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 CHANGED
@@ -2383,8 +2383,20 @@ ws
2383
2383
  .argument('<id>', 'Worksheet ID')
2384
2384
  .action(async (id) => {
2385
2385
  try {
2386
- const data = await api('GET', `/api/worksheets/${id}`);
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
- console.log(` ${chalk.cyan(a.description)}${progress} ${a.member_name || 'unassigned'} ${a.id}`);
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
- console.log(` ${icon} ${e.agent_name}${e.source_type} (${e.status})${cost} ${e.id}`);
5872
+ // AUTON-PORTFOLIO-VIEWin 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
- console.error(chalk.red(err.message));
5792
- process.exit(1);
5885
+ handleScopedError(err);
5793
5886
  }
5794
5887
  });
5795
5888
  executions