@formigio/fazemos-cli 0.10.45 → 0.10.47

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.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * FC-B — Founder Console: Close the Loop
3
+ *
4
+ * Exports `registerApprovalsCommands(program)` which wires two sub-commands
5
+ * into the root Commander program (mirrors src/governance.ts:518
6
+ * registerGovernanceCommands pattern):
7
+ *
8
+ * fazemos approvals list [--project <slug>] [--json]
9
+ * → GET /api/control-plane/approvals
10
+ * Returns pending approval gates for the active org.
11
+ *
12
+ * fazemos approvals resolve <id> --decision approve|reject --reason <text> [--json]
13
+ * → POST /api/control-plane/approvals/:id/resolve
14
+ * Resolves an approval gate; sends surface='cli' for audit trail.
15
+ * --reason is mandatory (min 4 chars) — this is the CLI typed-token
16
+ * equivalent (OBD #1 / rul_cli_resolve_terminal_explicit_is_typed_token_equivalent).
17
+ *
18
+ * Auth: requireAuth + org membership (X-Org-Id via api() helper).
19
+ * Both commands are org-scoped (noProjectHeader: true on resolve;
20
+ * list passes project_id as query param when --project provided).
21
+ *
22
+ * Rule: Server enforces reject + missing reason → 422 REJECT_REQUIRES_REASON.
23
+ * CLI also validates locally so the error is surfaced before any HTTP call.
24
+ *
25
+ * Spec: FC-B-founder-console-close-the-loop-manifest.yaml §cli
26
+ * Tech: FC-B-founder-console-close-the-loop-tech-spec.md §cli
27
+ */
28
+ import type { Command } from 'commander';
29
+ /**
30
+ * Register `approvals list` and `approvals resolve` into the root Commander program.
31
+ * Called from index.ts alongside registerGovernanceCommands.
32
+ *
33
+ * Pattern: mirrors registerGovernanceCommands at fazemos-cli/src/governance.ts:518.
34
+ */
35
+ export declare function registerApprovalsCommands(program: Command): void;
@@ -0,0 +1,279 @@
1
+ /**
2
+ * FC-B — Founder Console: Close the Loop
3
+ *
4
+ * Exports `registerApprovalsCommands(program)` which wires two sub-commands
5
+ * into the root Commander program (mirrors src/governance.ts:518
6
+ * registerGovernanceCommands pattern):
7
+ *
8
+ * fazemos approvals list [--project <slug>] [--json]
9
+ * → GET /api/control-plane/approvals
10
+ * Returns pending approval gates for the active org.
11
+ *
12
+ * fazemos approvals resolve <id> --decision approve|reject --reason <text> [--json]
13
+ * → POST /api/control-plane/approvals/:id/resolve
14
+ * Resolves an approval gate; sends surface='cli' for audit trail.
15
+ * --reason is mandatory (min 4 chars) — this is the CLI typed-token
16
+ * equivalent (OBD #1 / rul_cli_resolve_terminal_explicit_is_typed_token_equivalent).
17
+ *
18
+ * Auth: requireAuth + org membership (X-Org-Id via api() helper).
19
+ * Both commands are org-scoped (noProjectHeader: true on resolve;
20
+ * list passes project_id as query param when --project provided).
21
+ *
22
+ * Rule: Server enforces reject + missing reason → 422 REJECT_REQUIRES_REASON.
23
+ * CLI also validates locally so the error is surfaced before any HTTP call.
24
+ *
25
+ * Spec: FC-B-founder-console-close-the-loop-manifest.yaml §cli
26
+ * Tech: FC-B-founder-console-close-the-loop-tech-spec.md §cli
27
+ */
28
+ import chalk from 'chalk';
29
+ import { api, ApiError, resolveProjectIdBySlug } from './api.js';
30
+ // ── Helpers ────────────────────────────────────────────────────────────────────
31
+ /**
32
+ * Validate that the --decision flag is one of the allowed values.
33
+ * Throws a user-facing error otherwise.
34
+ */
35
+ function validateDecision(value) {
36
+ if (value !== 'approve' && value !== 'reject') {
37
+ throw new Error(`--decision must be 'approve' or 'reject', got: ${value}`);
38
+ }
39
+ return value;
40
+ }
41
+ /**
42
+ * Validate that --reason meets the minimum length requirement (4 chars).
43
+ * Throws a user-facing error otherwise.
44
+ * This is the CLI side of rul_cli_resolve_terminal_explicit_is_typed_token_equivalent.
45
+ */
46
+ function validateReason(value) {
47
+ const trimmed = value.trim();
48
+ if (trimmed.length < 4) {
49
+ throw new Error(`--reason must be at least 4 characters (got ${trimmed.length}). ` +
50
+ `Provide a meaningful reason — it lands in the audit trail.`);
51
+ }
52
+ return trimmed;
53
+ }
54
+ /**
55
+ * Extract the approval gates array from the API response envelope.
56
+ * The API may return the array under different keys depending on version.
57
+ */
58
+ function extractGates(data) {
59
+ if (Array.isArray(data.approvalRequests))
60
+ return data.approvalRequests;
61
+ if (Array.isArray(data.approval_requests))
62
+ return data.approval_requests;
63
+ if (Array.isArray(data.gates))
64
+ return data.gates;
65
+ // Some responses may return the array directly at a named key — scan.
66
+ for (const val of Object.values(data)) {
67
+ if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'object' && 'id' in val[0]) {
68
+ return val;
69
+ }
70
+ }
71
+ return [];
72
+ }
73
+ /**
74
+ * Render a human-readable table for approval gates.
75
+ * Format mirrors the governance waiting_on_me approval block (F34 pattern).
76
+ */
77
+ function renderGatesList(gates) {
78
+ if (gates.length === 0) {
79
+ console.log(chalk.dim(' No pending approval requests.'));
80
+ return;
81
+ }
82
+ console.log(chalk.bold(`Pending Approvals (${gates.length}):`));
83
+ console.log();
84
+ for (const gate of gates) {
85
+ console.log(` ${chalk.bold(gate.description || chalk.dim('(no description)'))}`);
86
+ console.log(` id: ${chalk.dim(gate.id)}`);
87
+ console.log(` status: ${chalk.yellow(gate.status)}`);
88
+ if (gate.pipeline_name) {
89
+ console.log(` pipeline: ${chalk.cyan(gate.pipeline_name)}`);
90
+ }
91
+ if (gate.step_name) {
92
+ console.log(` step: ${chalk.cyan(gate.step_name)}`);
93
+ }
94
+ if (gate.deadline_at) {
95
+ console.log(` deadline: ${new Date(gate.deadline_at).toLocaleString()}`);
96
+ }
97
+ console.log(` created: ${new Date(gate.created_at).toLocaleString()}`);
98
+ if (gate.project_id) {
99
+ console.log(` project: ${chalk.dim(gate.project_id)}`);
100
+ }
101
+ console.log();
102
+ }
103
+ console.log(chalk.dim(`To resolve: fazemos approvals resolve <id> --decision approve|reject --reason "<text>"`));
104
+ }
105
+ // ── Command registration ───────────────────────────────────────────────────────
106
+ /**
107
+ * Register `approvals list` and `approvals resolve` into the root Commander program.
108
+ * Called from index.ts alongside registerGovernanceCommands.
109
+ *
110
+ * Pattern: mirrors registerGovernanceCommands at fazemos-cli/src/governance.ts:518.
111
+ */
112
+ export function registerApprovalsCommands(program) {
113
+ // ── `fazemos approvals` ───────────────────────────────────────────────────
114
+ //
115
+ // Parent command hosting list + resolve sub-commands.
116
+ // Org-scoped (X-Org-Id sent by api() helper; no X-Fazemos-Project-Id).
117
+ // Auth: requireAuth + org membership (server-side, F37 shipped).
118
+ const approvalsCmd = program
119
+ .command('approvals')
120
+ .description('Approval gate management — list pending gates and resolve them from the CLI.\n\n' +
121
+ 'Sub-commands:\n' +
122
+ ' list List pending approval gates for your org\n' +
123
+ ' resolve Approve or reject an approval gate\n\n' +
124
+ 'Auth: your org is resolved via X-Org-Id (your active org); ' +
125
+ 'only gate approvers (per F37) may resolve.\n\n' +
126
+ 'Rule (OBD #1): --reason is mandatory for resolve — it is the CLI equivalent\n' +
127
+ 'of the typed-token ceremony (fully auditable via via=\'cli\' in audit trail).');
128
+ // ── `fazemos approvals list` ──────────────────────────────────────────────
129
+ //
130
+ // GET /api/control-plane/approvals
131
+ //
132
+ // Returns pending approval gates. Optional --project <slug> restricts to
133
+ // gates belonging to a specific project. No X-Fazemos-Project-Id header
134
+ // (control-plane endpoint); project_id passed as query param when provided.
135
+ //
136
+ // Usage:
137
+ // fazemos approvals list
138
+ // fazemos approvals list --project my-project
139
+ // fazemos approvals list --json
140
+ approvalsCmd
141
+ .command('list')
142
+ .description('List pending approval gates for your org.\n\n' +
143
+ 'Calls GET /api/control-plane/approvals (listOpenApprovals — F37 shipped).\n\n' +
144
+ 'Use --project to restrict results to a specific project.\n' +
145
+ 'Use --json for machine-readable output (raw API response).\n\n' +
146
+ 'To resolve a gate: fazemos approvals resolve <id> --decision approve|reject --reason "<text>"')
147
+ .option('--project <slug>', 'filter to a specific project (slug or id)')
148
+ .option('--json', 'output machine-readable JSON (raw API response)')
149
+ .action(async (opts) => {
150
+ try {
151
+ // Build the query string. project_id is passed as a query param
152
+ // (not via X-Fazemos-Project-Id header — this is a control-plane call).
153
+ let path = '/api/control-plane/approvals';
154
+ if (opts.project) {
155
+ // Resolve slug → id (handles UUID pass-through transparently).
156
+ // resolveProjectIdBySlug uses cached /auth/me; refreshes on miss.
157
+ const projectId = await resolveProjectIdBySlug(opts.project);
158
+ if (projectId) {
159
+ path += `?project_id=${encodeURIComponent(projectId)}`;
160
+ }
161
+ }
162
+ const data = await api('GET', path, undefined, { noProjectHeader: true });
163
+ if (opts.json) {
164
+ console.log(JSON.stringify(data, null, 2));
165
+ return;
166
+ }
167
+ const gates = extractGates(data);
168
+ renderGatesList(gates);
169
+ }
170
+ catch (err) {
171
+ if (err instanceof ApiError) {
172
+ console.error(chalk.red(`Error (${err.status}): ${err.message}`));
173
+ if (err.code) {
174
+ console.error(chalk.dim(` code: ${err.code}`));
175
+ }
176
+ }
177
+ else {
178
+ const msg = err instanceof Error ? err.message : String(err);
179
+ console.error(chalk.red(`Error: ${msg}`));
180
+ }
181
+ process.exit(1);
182
+ }
183
+ });
184
+ // ── `fazemos approvals resolve <id>` ─────────────────────────────────────
185
+ //
186
+ // POST /api/control-plane/approvals/:id/resolve
187
+ //
188
+ // Resolves an approval gate (approve or reject). Sends surface='cli' so the
189
+ // audit trail records via='cli' (rul_via_cli_landed_in_audit_trail).
190
+ //
191
+ // --decision and --reason are both required. --reason must be ≥ 4 chars.
192
+ // This constitutes the CLI typed-token equivalent (OBD #1):
193
+ // "Terminal-explicit command + mandatory --reason IS the CLI equivalent
194
+ // of typed-token ceremony. No hidden state. Fully auditable via='cli'."
195
+ //
196
+ // Server additionally enforces: reject + missing/empty reason_note → 422
197
+ // REJECT_REQUIRES_REASON (rul_cli_terminal_explicit_mandatory_reason).
198
+ //
199
+ // Usage:
200
+ // fazemos approvals resolve <id> --decision approve --reason "Looks good, ship it"
201
+ // fazemos approvals resolve <id> --decision reject --reason "Missing QA sign-off"
202
+ // fazemos approvals resolve <id> --decision approve --reason "ok" --json
203
+ approvalsCmd
204
+ .command('resolve')
205
+ .description('Approve or reject an approval gate.\n\n' +
206
+ 'Calls POST /api/control-plane/approvals/:id/resolve with surface=\'cli\'.\n' +
207
+ 'Sends via=\'cli\' to the audit trail (rul_via_cli_landed_in_audit_trail).\n\n' +
208
+ 'Both --decision and --reason are required.\n' +
209
+ '--reason must be at least 4 characters and is recorded in the audit trail.\n' +
210
+ 'This is the CLI equivalent of the typed-token ceremony (OBD #1).\n\n' +
211
+ 'Exit codes:\n' +
212
+ ' 0 — resolved successfully\n' +
213
+ ' 1 — API error (see error message)\n' +
214
+ ' 2 — validation error (missing/invalid flag)')
215
+ .argument('<id>', 'Approval gate ID (UUID)')
216
+ .requiredOption('--decision <approve|reject>', 'Decision: approve or reject')
217
+ .requiredOption('--reason <text>', 'Reason for the decision (min 4 chars; recorded in audit trail)')
218
+ .option('--json', 'output machine-readable JSON (raw API response)')
219
+ .action(async (id, opts) => {
220
+ try {
221
+ // ── Client-side validation ─────────────────────────────────────────
222
+ // Validate before making any HTTP call (fast fail, clear messages).
223
+ // Server-side re-validates as well (defense-in-depth).
224
+ const decision = validateDecision(opts.decision);
225
+ const reason = validateReason(opts.reason);
226
+ // ── POST /api/control-plane/approvals/:id/resolve ──────────────────
227
+ // Body matches the FC-B modified contract:
228
+ // { decision, reason_note, surface: 'cli' }
229
+ // surface='cli' propagates to via='cli' in the audit trail.
230
+ const body = {
231
+ decision,
232
+ reason_note: reason,
233
+ surface: 'cli',
234
+ };
235
+ const data = await api('POST', `/api/control-plane/approvals/${encodeURIComponent(id)}/resolve`, body, { noProjectHeader: true });
236
+ if (opts.json) {
237
+ console.log(JSON.stringify(data, null, 2));
238
+ return;
239
+ }
240
+ // ── Human output ───────────────────────────────────────────────────
241
+ const decisionLabel = decision === 'approve'
242
+ ? chalk.green('approved')
243
+ : chalk.red('rejected');
244
+ console.log(chalk.bold(`Approval ${decisionLabel}`));
245
+ console.log();
246
+ console.log(` id: ${chalk.dim(data.id)}`);
247
+ console.log(` decision: ${decisionLabel}`);
248
+ console.log(` via: ${chalk.dim(data.via)}`);
249
+ console.log(` resolved_at: ${new Date(data.resolved_at).toLocaleString()}`);
250
+ if (data.downstream_queued) {
251
+ console.log();
252
+ console.log(` ${chalk.cyan('→ downstream step queued:')} ${chalk.dim(data.downstream_queued)}`);
253
+ }
254
+ console.log();
255
+ }
256
+ catch (err) {
257
+ if (err instanceof ApiError) {
258
+ // Surface server-enforced errors clearly (e.g., 422 REJECT_REQUIRES_REASON,
259
+ // 409 APPROVAL_ALREADY_RESOLVED, 404 APPROVAL_GATE_NOT_FOUND).
260
+ console.error(chalk.red(`Error (${err.status}): ${err.message}`));
261
+ if (err.code) {
262
+ console.error(chalk.dim(` code: ${err.code}`));
263
+ }
264
+ if (err.code === 'APPROVAL_ALREADY_RESOLVED') {
265
+ console.error(chalk.dim(' This gate was already resolved — nothing changed.'));
266
+ }
267
+ else if (err.code === 'REJECT_REQUIRES_REASON') {
268
+ console.error(chalk.dim(' Server requires a reason for rejection. Pass --reason "<text>" (min 4 chars).'));
269
+ }
270
+ }
271
+ else {
272
+ const msg = err instanceof Error ? err.message : String(err);
273
+ console.error(chalk.red(`Error: ${msg}`));
274
+ }
275
+ process.exit(1);
276
+ }
277
+ });
278
+ }
279
+ //# sourceMappingURL=approvals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -84,6 +84,17 @@ export declare function addEnvironment(name: string, env: {
84
84
  cognitoRegion: string;
85
85
  }): void;
86
86
  export declare function hasEnvironments(): boolean;
87
+ /** Set the in-memory environment override from the global `--env` flag (or null to clear). */
88
+ export declare function setEnvOverride(name: string | null): void;
89
+ export type EnvSource = 'flag' | 'session' | 'persisted' | 'none';
90
+ /** Where the effective environment name came from, for display in whoami/env. */
91
+ export declare function getActiveEnvSource(): EnvSource;
92
+ /**
93
+ * The effective environment name for this process, honoring the precedence
94
+ * chain above. Returns '' when nothing is configured (callers surface a clear
95
+ * "no environment" error via getEnv).
96
+ */
97
+ export declare function getActiveEnvName(): string;
87
98
  export declare function getEnv(): {
88
99
  apiUrl: string;
89
100
  cognitoPoolId: string;
package/dist/config.js CHANGED
@@ -22,15 +22,58 @@ export function addEnvironment(name, env) {
22
22
  export function hasEnvironments() {
23
23
  return Object.keys(config.get('environments')).length > 0;
24
24
  }
25
+ // ── Session-scoped environment override ─────────────────────
26
+ //
27
+ // The persisted `activeEnv` lives in a single on-disk `conf` file shared by
28
+ // every session on the machine, so a bare `fazemos env <name>` in one session
29
+ // clobbers another. To let concurrent agents target different environments
30
+ // without stomping each other (the CLI-DX-1 "credentials keyed only by env"
31
+ // gap), the effective environment is resolved per-process:
32
+ //
33
+ // --env <name> flag > FAZEMOS_ENV env var > persisted activeEnv
34
+ //
35
+ // The flag override is set once at CLI bootstrap (see the preAction hook in
36
+ // src/index.ts). Environment is deliberately session-scoped — not written into
37
+ // a git-committable `.fazemos.json` like org/project — because dev/prod is a
38
+ // per-session choice that shouldn't be pinned in a shared repo file.
39
+ let envOverride = null;
40
+ /** Set the in-memory environment override from the global `--env` flag (or null to clear). */
41
+ export function setEnvOverride(name) {
42
+ envOverride = name?.trim() || null;
43
+ }
44
+ /** Where the effective environment name came from, for display in whoami/env. */
45
+ export function getActiveEnvSource() {
46
+ if (envOverride)
47
+ return 'flag';
48
+ if (process.env.FAZEMOS_ENV?.trim())
49
+ return 'session';
50
+ if (config.get('activeEnv'))
51
+ return 'persisted';
52
+ return 'none';
53
+ }
54
+ /**
55
+ * The effective environment name for this process, honoring the precedence
56
+ * chain above. Returns '' when nothing is configured (callers surface a clear
57
+ * "no environment" error via getEnv).
58
+ */
59
+ export function getActiveEnvName() {
60
+ return envOverride || process.env.FAZEMOS_ENV?.trim() || config.get('activeEnv');
61
+ }
25
62
  export function getEnv() {
26
- const name = config.get('activeEnv');
63
+ const name = getActiveEnvName();
64
+ if (!name) {
65
+ throw new Error('No environment configured. Run: fazemos init <name> --api-url <url>');
66
+ }
27
67
  const env = config.get('environments')[name];
28
- if (!env)
29
- throw new Error(`Environment "${name}" not configured`);
68
+ if (!env) {
69
+ const src = getActiveEnvSource();
70
+ const via = src === 'flag' ? ' (from --env)' : src === 'session' ? ' (from FAZEMOS_ENV)' : '';
71
+ throw new Error(`Environment "${name}"${via} is not configured. Configure it with: fazemos init ${name} --api-url <url>`);
72
+ }
30
73
  return { name, ...env };
31
74
  }
32
75
  export function getToken() {
33
- const env = config.get('activeEnv');
76
+ const env = getActiveEnvName();
34
77
  const auth = config.get('auth')[env];
35
78
  if (!auth)
36
79
  return null;
@@ -123,7 +166,7 @@ export function getAuthMeCache() {
123
166
  const cache = config.get('authMeCache');
124
167
  if (!cache)
125
168
  return null;
126
- const activeEnv = config.get('activeEnv');
169
+ const activeEnv = getActiveEnvName();
127
170
  if (cache.activeEnv !== activeEnv)
128
171
  return null;
129
172
  if (Date.now() - cache.fetchedAt > AUTH_ME_CACHE_TTL_MS)
@@ -131,7 +174,7 @@ export function getAuthMeCache() {
131
174
  return cache.data;
132
175
  }
133
176
  export function setAuthMeCache(data) {
134
- const activeEnv = config.get('activeEnv');
177
+ const activeEnv = getActiveEnvName();
135
178
  config.set('authMeCache', {
136
179
  activeEnv,
137
180
  fetchedAt: Date.now(),
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import chalk from 'chalk';
4
- import { config, getEnv, getToken, getActiveOrgId, setActiveOrgId, addEnvironment, hasEnvironments,
4
+ import { config, getEnv, getToken, getActiveEnvName, getActiveEnvSource, setEnvOverride, getActiveOrgId, setActiveOrgId, addEnvironment, hasEnvironments,
5
5
  // F15 — project context helpers
6
6
  getActiveProjectId, setActiveProjectId, clearActiveProjectId, findProjectBySlug, findProjectById, findOrgById,
7
7
  // Directory-scoped context override (.fazemos.json)
@@ -24,6 +24,7 @@ import { registerGovernanceCommands } from './governance.js';
24
24
  import { registerAutoStartCommands } from './autostart.js';
25
25
  import { registerFtrCommand } from './ftr.js';
26
26
  import { registerScheduleCommands } from './schedule.js';
27
+ import { registerApprovalsCommands } from './approvals.js';
27
28
  import { parseExecutionsJson, resolveWaitOptions, waitForPipelines, buildAwsCommand, validateExecutionEntry, } from './wait-for-pipeline.js';
28
29
  import { readFileSync, readdirSync, writeFileSync, mkdirSync, existsSync, statSync } from 'fs';
29
30
  import { fileURLToPath } from 'url';
@@ -198,7 +199,12 @@ const program = new Command();
198
199
  program
199
200
  .name('fazemos')
200
201
  .description('Fazemos CLI — Team Accomplishment Platform')
201
- .version(pkg.version);
202
+ .version(pkg.version)
203
+ // Session-scoped environment override. Precedence: this flag > FAZEMOS_ENV
204
+ // env var > the persisted active env. Lets one command (or, via the env var,
205
+ // a whole session) target a different environment without a shared-disk
206
+ // `fazemos env` switch that would clobber a concurrent session.
207
+ .option('--env <name>', 'Environment to use for this invocation (overrides FAZEMOS_ENV and the persisted active env)');
202
208
  // ── Directory-scoped default org/project (.fazemos.json) ─────
203
209
  //
204
210
  // Before any command action runs, discover the nearest `.fazemos.json`
@@ -265,6 +271,11 @@ async function applyDirContext(topCommand) {
265
271
  }
266
272
  }
267
273
  program.hook('preAction', async (_thisCommand, actionCommand) => {
274
+ // Apply the global `--env` flag first so every downstream resolution (dir
275
+ // context slug lookups, auth, API calls) sees the right environment.
276
+ const envFlag = program.opts().env;
277
+ if (envFlag)
278
+ setEnvOverride(envFlag);
268
279
  // Resolve the top-level command name (e.g. `worksheets list` → `worksheets`).
269
280
  let c = actionCommand;
270
281
  let top = c?.name?.();
@@ -331,8 +342,8 @@ program
331
342
  // ── Environment ─────────────────────────────────────────────
332
343
  program
333
344
  .command('env')
334
- .description('Show or switch environment')
335
- .argument('[name]', 'Environment to switch to')
345
+ .description('Show or switch environment. The persisted switch is machine-global; for per-session isolation set FAZEMOS_ENV or pass --env.')
346
+ .argument('[name]', 'Environment to switch to (persisted, machine-global)')
336
347
  .action((name) => {
337
348
  if (!hasEnvironments()) {
338
349
  console.log(chalk.yellow('No environments configured. Run: fazemos init <name> --api-url <url>'));
@@ -346,10 +357,25 @@ program
346
357
  process.exit(1);
347
358
  }
348
359
  config.set('activeEnv', name);
349
- console.log(chalk.green(`Switched to ${name}`));
360
+ console.log(chalk.green(`Switched persisted environment to ${name}`));
361
+ // A persisted switch is machine-global. If this session is pinned to a
362
+ // different env via the override chain, the switch won't take effect here
363
+ // until the override is cleared — say so rather than silently no-op'ing.
364
+ const src = getActiveEnvSource();
365
+ if ((src === 'session' || src === 'flag') && getActiveEnvName() !== name) {
366
+ const via = src === 'flag' ? '--env' : 'FAZEMOS_ENV';
367
+ console.log(chalk.yellow(`Note: this session is pinned to "${getActiveEnvName()}" via ${via}; ` +
368
+ `the persisted switch won't take effect here until that override is cleared.`));
369
+ }
350
370
  }
351
371
  const env = getEnv();
352
- console.log(` Environment: ${chalk.cyan(env.name)}`);
372
+ const src = getActiveEnvSource();
373
+ const srcLabel = src === 'flag'
374
+ ? chalk.gray(' (this invocation, via --env)')
375
+ : src === 'session'
376
+ ? chalk.gray(' (this session, via FAZEMOS_ENV)')
377
+ : '';
378
+ console.log(` Environment: ${chalk.cyan(env.name)}${srcLabel}`);
353
379
  console.log(` API: ${env.apiUrl}`);
354
380
  console.log(` Cognito: ${env.cognitoPoolId}`);
355
381
  const token = getToken();
@@ -432,11 +458,11 @@ auth
432
458
  .command('logout')
433
459
  .description('Clear stored credentials')
434
460
  .action(() => {
435
- const env = config.get('activeEnv');
461
+ const env = getActiveEnvName();
436
462
  const auths = config.get('auth');
437
463
  delete auths[env];
438
464
  config.set('auth', auths);
439
- console.log(chalk.green('Logged out'));
465
+ console.log(chalk.green(`Logged out of ${env}`));
440
466
  });
441
467
  // ── Whoami ──────────────────────────────────────────────────
442
468
  program
@@ -458,6 +484,13 @@ program
458
484
  }
459
485
  console.log(` User: ${chalk.cyan(data.user.email)}`);
460
486
  console.log(` Member: ${data.member.displayName} (${data.member.role})`);
487
+ const envSrc = getActiveEnvSource();
488
+ const envSrcLabel = envSrc === 'flag'
489
+ ? chalk.gray(' (this invocation, via --env)')
490
+ : envSrc === 'session'
491
+ ? chalk.gray(' (this session, via FAZEMOS_ENV)')
492
+ : '';
493
+ console.log(` Env: ${chalk.cyan(getActiveEnvName())}${envSrcLabel}`);
461
494
  const activeOrgId = getActiveOrgId() ?? data.activeOrgId;
462
495
  const activeOrg = data.orgs.find(o => o.id === activeOrgId);
463
496
  if (activeOrg) {
@@ -9620,6 +9653,13 @@ registerFtrCommand(program);
9620
9653
  // (does NOT pass noProjectHeader: true — greenfield fix per binding_decision.cli_header_scoping).
9621
9654
  // admin/owner required for set/unset/disable/enable/tz; any active member for list.
9622
9655
  registerScheduleCommands(program);
9656
+ // ── FC-B — Approval Gate CLI: approvals list / approvals resolve ──────────────
9657
+ // Registers `approvals` top-level command with `list` and `resolve` sub-commands.
9658
+ // Both calls are org-scoped (noProjectHeader: true); resolve sends surface='cli'
9659
+ // so the audit trail records via='cli' (rul_via_cli_landed_in_audit_trail).
9660
+ // --reason is mandatory for resolve — CLI typed-token equivalent (OBD #1).
9661
+ // Auth: requireAuth + org membership (server-side, F37 shipped).
9662
+ registerApprovalsCommands(program);
9623
9663
  // Skip auto-parse only when running under Vitest (which sets process.env.VITEST).
9624
9664
  // Tests import `program` and drive it via `program.parseAsync(...)` after mocking
9625
9665
  // `./api.js`. In every other context — direct invocation, npx tsx, OR the bin