@formigio/fazemos-cli 0.10.39 → 0.10.41

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,49 @@
1
+ /**
2
+ * AUTON-AUTOSTART — Action→Pipeline Auto-Start: CLI command helpers.
3
+ *
4
+ * Exports `registerAutoStartCommands(program)` which wires the following commands
5
+ * into the root Commander program (mirrors the trigger/wake group pattern):
6
+ *
7
+ * fazemos autostart list
8
+ * → GET /api/control-plane/autostart?project_id=<id>
9
+ * Reads the allow-listed (action_id, template_id, enabled) bindings.
10
+ *
11
+ * fazemos autostart status [--json]
12
+ * → GET /api/control-plane/autostart?project_id=<id>
13
+ * bindings + recent action_pipeline_fires (window_key, fire_status,
14
+ * pipeline_instance_id, last_error).
15
+ *
16
+ * fazemos autostart disable --action <action-id> [--until <iso>] [--reason <text>]
17
+ * → POST /api/control-plane/autostart/disable
18
+ * { action: "disable", project_id, worksheet_action_id, reason, until? }
19
+ * Upserts an action_autostart_disables row; timed (--until) or indefinite.
20
+ *
21
+ * fazemos autostart enable --action <action-id>
22
+ * → POST /api/control-plane/autostart/disable
23
+ * { action: "enable", project_id, worksheet_action_id }
24
+ * Deletes the disable row (re-enables the action's auto-start).
25
+ *
26
+ * Auth: disable/enable require admin/owner (same as trigger/wake).
27
+ * list/status require any active org member.
28
+ * Scope: project-scoped reads (project_id query param); writes are org+project.
29
+ *
30
+ * NOTE: `fire-now` is deliberately NOT offered in V1 — manual pipeline start is
31
+ * already `fazemos pl create`. A fire-now re-drive of a failed row is V2.
32
+ *
33
+ * Spec: AUTON-AUTOSTART-tech-spec.md §11 (R12, AC14)
34
+ * Manifest: cli_commands (fazemos autostart list/status/disable/enable)
35
+ *
36
+ * Key differences from trigger.ts:
37
+ * - No cadence dimension (action auto-start is per-action, per-window)
38
+ * - No fire-now command (V1 spec explicitly excludes it)
39
+ * - Bindings keyed by action_id + template_id (not role + cadence)
40
+ * - disable/enable keyed by --action <worksheet_action_id> (not --role + --cadence)
41
+ * - Endpoint: /api/control-plane/autostart and /api/control-plane/autostart/disable
42
+ */
43
+ import type { Command } from 'commander';
44
+ /**
45
+ * Register `autostart list`, `autostart status`, `autostart disable`, and
46
+ * `autostart enable` into the root Commander program. Called from index.ts
47
+ * alongside registerTriggerCommands and registerWakeCommands.
48
+ */
49
+ export declare function registerAutoStartCommands(program: Command): void;
@@ -0,0 +1,364 @@
1
+ /**
2
+ * AUTON-AUTOSTART — Action→Pipeline Auto-Start: CLI command helpers.
3
+ *
4
+ * Exports `registerAutoStartCommands(program)` which wires the following commands
5
+ * into the root Commander program (mirrors the trigger/wake group pattern):
6
+ *
7
+ * fazemos autostart list
8
+ * → GET /api/control-plane/autostart?project_id=<id>
9
+ * Reads the allow-listed (action_id, template_id, enabled) bindings.
10
+ *
11
+ * fazemos autostart status [--json]
12
+ * → GET /api/control-plane/autostart?project_id=<id>
13
+ * bindings + recent action_pipeline_fires (window_key, fire_status,
14
+ * pipeline_instance_id, last_error).
15
+ *
16
+ * fazemos autostart disable --action <action-id> [--until <iso>] [--reason <text>]
17
+ * → POST /api/control-plane/autostart/disable
18
+ * { action: "disable", project_id, worksheet_action_id, reason, until? }
19
+ * Upserts an action_autostart_disables row; timed (--until) or indefinite.
20
+ *
21
+ * fazemos autostart enable --action <action-id>
22
+ * → POST /api/control-plane/autostart/disable
23
+ * { action: "enable", project_id, worksheet_action_id }
24
+ * Deletes the disable row (re-enables the action's auto-start).
25
+ *
26
+ * Auth: disable/enable require admin/owner (same as trigger/wake).
27
+ * list/status require any active org member.
28
+ * Scope: project-scoped reads (project_id query param); writes are org+project.
29
+ *
30
+ * NOTE: `fire-now` is deliberately NOT offered in V1 — manual pipeline start is
31
+ * already `fazemos pl create`. A fire-now re-drive of a failed row is V2.
32
+ *
33
+ * Spec: AUTON-AUTOSTART-tech-spec.md §11 (R12, AC14)
34
+ * Manifest: cli_commands (fazemos autostart list/status/disable/enable)
35
+ *
36
+ * Key differences from trigger.ts:
37
+ * - No cadence dimension (action auto-start is per-action, per-window)
38
+ * - No fire-now command (V1 spec explicitly excludes it)
39
+ * - Bindings keyed by action_id + template_id (not role + cadence)
40
+ * - disable/enable keyed by --action <worksheet_action_id> (not --role + --cadence)
41
+ * - Endpoint: /api/control-plane/autostart and /api/control-plane/autostart/disable
42
+ */
43
+ import chalk from 'chalk';
44
+ import { api } from './api.js';
45
+ import { getActiveProjectId } from './config.js';
46
+ // ── Helpers ────────────────────────────────────────────────────────────────────
47
+ /**
48
+ * Validate an ISO 8601 timestamp string (used for --until on disable).
49
+ * The API is strict — malformed timestamps produce a 400.
50
+ */
51
+ function validateIsoTimestamp(value) {
52
+ const d = new Date(value);
53
+ if (isNaN(d.getTime())) {
54
+ throw new Error(`--until must be a valid ISO 8601 timestamp (e.g. 2026-12-31T23:59:59Z), got: ${value}`);
55
+ }
56
+ return value;
57
+ }
58
+ /**
59
+ * Human-readable fire_status label with colour.
60
+ * Mirrors the action_pipeline_fires.fire_status CHECK values:
61
+ * 'claimed' → in progress; 'fired' → created + launched; 'failed' → errored.
62
+ */
63
+ function fireStatusLabel(status) {
64
+ switch (status) {
65
+ case 'fired':
66
+ return chalk.green(status);
67
+ case 'failed':
68
+ return chalk.red(status);
69
+ case 'claimed':
70
+ return chalk.cyan(status);
71
+ default:
72
+ return chalk.dim(status);
73
+ }
74
+ }
75
+ // ── Command registration ───────────────────────────────────────────────────────
76
+ /**
77
+ * Register `autostart list`, `autostart status`, `autostart disable`, and
78
+ * `autostart enable` into the root Commander program. Called from index.ts
79
+ * alongside registerTriggerCommands and registerWakeCommands.
80
+ */
81
+ export function registerAutoStartCommands(program) {
82
+ // ── `fazemos autostart` ──────────────────────────────────────────────────────
83
+ //
84
+ // Parent command hosting sub-commands: list, status, disable, enable.
85
+ // The parent itself has no action (sub-commands always required).
86
+ const autoStartCmd = program
87
+ .command('autostart')
88
+ .description('Inspect and operate the action→pipeline auto-start control plane (AUTONOMY).\n\n' +
89
+ 'Sub-commands:\n' +
90
+ ' list List the allow-listed (action, template) auto-start bindings\n' +
91
+ ' status Show bindings + recent fire history (last 50 entries)\n' +
92
+ ' disable Disable auto-start for an action (timed or indefinite) (admin/owner)\n' +
93
+ ' enable Re-enable a previously disabled action auto-start (admin/owner)\n\n' +
94
+ 'list/status are readable by any active org member.\n' +
95
+ 'disable/enable require admin or owner role.\n' +
96
+ 'All operations are project-scoped (uses active project or --project override).\n\n' +
97
+ 'The auto-start engine is GATED OFF by default (AUTOSTART_ALLOWLIST empty).\n' +
98
+ 'An empty bindings list means the engine is dormant — this is the safe\n' +
99
+ 'default state. The ENABLE flip is a separate founder step.');
100
+ // ── `fazemos autostart list` ──────────────────────────────────────────────────
101
+ //
102
+ // GET /api/control-plane/autostart?project_id=<id>
103
+ //
104
+ // Prints the allow-listed (action_id, template_id) bindings + enabled state.
105
+ // No --json flag needed (list is simple enough for human output only).
106
+ //
107
+ // Usage:
108
+ // fazemos autostart list
109
+ autoStartCmd
110
+ .command('list')
111
+ .description('List the configured auto-start bindings for the active project.\n\n' +
112
+ 'Shows each allow-listed (action_id, template_id) pair and whether it is\n' +
113
+ 'currently enabled. The binding list is driven by AUTOSTART_ALLOWLIST on\n' +
114
+ 'the API; the enabled state reflects any operator disable in effect.\n\n' +
115
+ 'An empty list means the engine is dormant (default safe state).\n\n' +
116
+ 'Readable by any active org member.')
117
+ .action(async () => {
118
+ try {
119
+ const projectId = getActiveProjectId();
120
+ if (!projectId) {
121
+ throw new Error('No active project. Run `fazemos projects switch <slug>` first.');
122
+ }
123
+ const data = await api('GET', `/api/control-plane/autostart?project_id=${encodeURIComponent(projectId)}`, undefined, { noProjectHeader: true });
124
+ const { bindings } = data;
125
+ if (bindings.length === 0) {
126
+ console.log(chalk.yellow('No auto-start bindings configured (AUTOSTART_ALLOWLIST is empty or not set).\n' +
127
+ 'The engine is dormant — this is the default safe state.'));
128
+ return;
129
+ }
130
+ console.log(chalk.bold(`Auto-start bindings (${bindings.length} entries):`));
131
+ console.log();
132
+ for (const binding of bindings) {
133
+ const enabledLabel = binding.enabled
134
+ ? chalk.green('enabled')
135
+ : chalk.red('disabled');
136
+ console.log(` ${chalk.cyan(binding.action_id)} ${enabledLabel}`);
137
+ console.log(` template: ${chalk.dim(binding.template_id)}`);
138
+ console.log();
139
+ }
140
+ }
141
+ catch (err) {
142
+ const msg = err instanceof Error ? err.message : String(err);
143
+ console.error(chalk.red(`Error: ${msg}`));
144
+ process.exit(1);
145
+ }
146
+ });
147
+ // ── `fazemos autostart status` ────────────────────────────────────────────────
148
+ //
149
+ // GET /api/control-plane/autostart?project_id=<id>
150
+ //
151
+ // bindings + recent action_pipeline_fires (window_key, fire_status,
152
+ // pipeline_instance_id, last_error, claimed_at, fired_at, failed_at).
153
+ // Fires capped at 50 most recent entries (ordered by claimed_at DESC) by the API.
154
+ //
155
+ // Usage:
156
+ // fazemos autostart status
157
+ // fazemos autostart status --json
158
+ autoStartCmd
159
+ .command('status')
160
+ .description('Show auto-start bindings and recent fire history for the active project.\n\n' +
161
+ 'Prints the allow-listed (action, template) bindings alongside the last 50\n' +
162
+ 'action_pipeline_fire records (window_key, fire_status, pipeline_instance_id).\n\n' +
163
+ 'fire_status values:\n' +
164
+ ' claimed — claim inserted; pipeline create + launch in progress\n' +
165
+ ' fired — pipeline instance created and launched successfully\n' +
166
+ ' failed — the create/launch attempt failed (see last_error)\n\n' +
167
+ 'Note: suppressed fires do NOT appear in the ledger (the claim is deleted on\n' +
168
+ 'suppression; `autostart_suppressed` is logged as an agent_event instead).\n\n' +
169
+ 'Readable by any active org member. Use --json for machine-readable output.')
170
+ .option('--json', 'output machine-readable JSON')
171
+ .action(async (opts) => {
172
+ try {
173
+ const projectId = getActiveProjectId();
174
+ if (!projectId) {
175
+ throw new Error('No active project. Run `fazemos projects switch <slug>` first.');
176
+ }
177
+ const data = await api('GET', `/api/control-plane/autostart?project_id=${encodeURIComponent(projectId)}`, undefined, { noProjectHeader: true });
178
+ if (opts.json) {
179
+ console.log(JSON.stringify(data, null, 2));
180
+ return;
181
+ }
182
+ const { bindings, fires } = data;
183
+ // ── Bindings ──────────────────────────────────────────────────────────
184
+ if (bindings.length === 0) {
185
+ console.log(chalk.yellow('No auto-start bindings configured (AUTOSTART_ALLOWLIST is empty or not set).'));
186
+ }
187
+ else {
188
+ console.log(chalk.bold(`Auto-start bindings (${bindings.length} entries):`));
189
+ console.log();
190
+ for (const binding of bindings) {
191
+ const enabledLabel = binding.enabled
192
+ ? chalk.green('enabled')
193
+ : chalk.red('disabled');
194
+ console.log(` ${chalk.cyan(binding.action_id)} ${enabledLabel}`);
195
+ console.log(` template: ${chalk.dim(binding.template_id)}`);
196
+ }
197
+ console.log();
198
+ }
199
+ // ── Recent fires ──────────────────────────────────────────────────────
200
+ if (fires.length === 0) {
201
+ console.log(chalk.dim('No recent auto-start fires.'));
202
+ return;
203
+ }
204
+ console.log(chalk.bold(`Recent auto-start fires (${fires.length}):`));
205
+ console.log();
206
+ for (const fire of fires) {
207
+ const statusLabel = fireStatusLabel(fire.fire_status);
208
+ console.log(` ${chalk.cyan(fire.worksheet_action_id.slice(0, 8) + '…')} window: ${fire.window_key} ${statusLabel}`);
209
+ if (fire.pipeline_instance_id) {
210
+ console.log(` pipeline: ${chalk.dim(fire.pipeline_instance_id)}`);
211
+ }
212
+ if (fire.template_id) {
213
+ console.log(` template: ${chalk.dim(fire.template_id)}`);
214
+ }
215
+ if (fire.last_error) {
216
+ console.log(` error: ${chalk.red(fire.last_error)}`);
217
+ }
218
+ console.log(` claimed: ${new Date(fire.claimed_at).toLocaleString()}`);
219
+ if (fire.fired_at) {
220
+ console.log(` fired: ${new Date(fire.fired_at).toLocaleString()}`);
221
+ }
222
+ if (fire.failed_at) {
223
+ console.log(` failed: ${chalk.red(new Date(fire.failed_at).toLocaleString())}`);
224
+ }
225
+ console.log(` id: ${chalk.dim(fire.id)}`);
226
+ console.log();
227
+ }
228
+ }
229
+ catch (err) {
230
+ const msg = err instanceof Error ? err.message : String(err);
231
+ console.error(chalk.red(`Error: ${msg}`));
232
+ process.exit(1);
233
+ }
234
+ });
235
+ // ── `fazemos autostart disable` ───────────────────────────────────────────────
236
+ //
237
+ // POST /api/control-plane/autostart/disable { action: "disable", ... }
238
+ //
239
+ // Upserts an action_autostart_disables row the sweep consults before claiming.
240
+ // Active disable → action not eligible → no claim, no fire.
241
+ // --until <iso>: timed disable; omit for indefinite until `fazemos autostart enable`.
242
+ //
243
+ // Note: does NOT delete the action from the AUTOSTART_ALLOWLIST (that is an
244
+ // SSM / deployment-level change). The disable row is the operator kill switch.
245
+ //
246
+ // Usage:
247
+ // fazemos autostart disable --action <action-id> --reason "Pausing during Q2 freeze"
248
+ // fazemos autostart disable --action <action-id> --until 2026-12-31T23:59:59Z --reason "Q2 freeze"
249
+ autoStartCmd
250
+ .command('disable')
251
+ .description('Disable auto-start for an action (admin/owner only).\n\n' +
252
+ 'Upserts a disable record for the given action. While disabled, the auto-start\n' +
253
+ 'sweep skips this action — no claim, no fire. The action\'s readiness state\n' +
254
+ '(auto_start=\'ready\') is preserved; re-enabling allows the next sweep to fire.\n\n' +
255
+ ' (no --until) Indefinite disable — remains until `fazemos autostart enable`\n' +
256
+ ' --until <iso> Timed disable — auto-expires at the given UTC timestamp\n\n' +
257
+ 'Requires admin or owner role. --reason is mandatory (audit trail).')
258
+ .requiredOption('--action <id>', 'worksheet_action_id (UUID) to disable')
259
+ .requiredOption('--reason <text>', 'reason for the disable (required for audit trail)')
260
+ .option('--until <iso>', 'ISO 8601 timestamp when the disable expires (omit for indefinite)')
261
+ .option('--json', 'output machine-readable JSON')
262
+ .action(async (opts) => {
263
+ try {
264
+ const projectId = getActiveProjectId();
265
+ if (!projectId) {
266
+ throw new Error('No active project. Run `fazemos projects switch <slug>` first.');
267
+ }
268
+ const body = {
269
+ action: 'disable',
270
+ project_id: projectId,
271
+ worksheet_action_id: opts.action.trim(),
272
+ reason: opts.reason,
273
+ };
274
+ if (opts.until !== undefined) {
275
+ body.until = validateIsoTimestamp(opts.until);
276
+ }
277
+ const data = await api('POST', '/api/control-plane/autostart/disable', body, {
278
+ noProjectHeader: true,
279
+ });
280
+ if (opts.json) {
281
+ console.log(JSON.stringify(data, null, 2));
282
+ return;
283
+ }
284
+ if (data.disabled) {
285
+ console.log(chalk.red(`⏸ Auto-start disabled for action: ${chalk.cyan(opts.action.trim())}`));
286
+ if (opts.until) {
287
+ console.log(` Expires at: ${new Date(opts.until).toLocaleString()}`);
288
+ }
289
+ else {
290
+ console.log(` Duration: indefinite (run \`fazemos autostart enable --action ${opts.action.trim()}\` to re-enable)`);
291
+ }
292
+ console.log(` Reason: ${opts.reason}`);
293
+ console.log();
294
+ console.log(chalk.dim(' The action remains in the AUTOSTART_ALLOWLIST.'));
295
+ console.log(chalk.dim(' The sweep will skip it until the disable is removed or expires.'));
296
+ }
297
+ else {
298
+ console.log(chalk.yellow(`No change — auto-start was already disabled for action ${opts.action.trim()}.`));
299
+ }
300
+ }
301
+ catch (err) {
302
+ const msg = err instanceof Error ? err.message : String(err);
303
+ console.error(chalk.red(`Error: ${msg}`));
304
+ process.exit(1);
305
+ }
306
+ });
307
+ // ── `fazemos autostart enable` ────────────────────────────────────────────────
308
+ //
309
+ // POST /api/control-plane/autostart/disable { action: "enable", ... }
310
+ //
311
+ // Deletes the action_autostart_disables row (re-enables the action's auto-start).
312
+ // After enable: the next sweep tick that finds this action ready + allow-listed
313
+ // will claim and fire as normal (subject to windowed-claim and live-pipeline checks).
314
+ //
315
+ // Usage:
316
+ // fazemos autostart enable --action <action-id>
317
+ autoStartCmd
318
+ .command('enable')
319
+ .description('Re-enable auto-start for a previously disabled action (admin/owner only).\n\n' +
320
+ 'Deletes the disable record for the given action, allowing the next auto-start\n' +
321
+ 'sweep tick to evaluate and fire it (subject to the windowed-claim and\n' +
322
+ 'live-pipeline idempotency checks — one pipeline per window maximum).\n\n' +
323
+ 'Requires admin or owner role.')
324
+ .requiredOption('--action <id>', 'worksheet_action_id (UUID) to re-enable')
325
+ .option('--reason <text>', 'reason for re-enabling (optional; recorded in audit trail)')
326
+ .option('--json', 'output machine-readable JSON')
327
+ .action(async (opts) => {
328
+ try {
329
+ const projectId = getActiveProjectId();
330
+ if (!projectId) {
331
+ throw new Error('No active project. Run `fazemos projects switch <slug>` first.');
332
+ }
333
+ const body = {
334
+ action: 'enable',
335
+ project_id: projectId,
336
+ worksheet_action_id: opts.action.trim(),
337
+ };
338
+ if (opts.reason !== undefined) {
339
+ body.reason = opts.reason;
340
+ }
341
+ const data = await api('POST', '/api/control-plane/autostart/disable', body, {
342
+ noProjectHeader: true,
343
+ });
344
+ if (opts.json) {
345
+ console.log(JSON.stringify(data, null, 2));
346
+ return;
347
+ }
348
+ if (data.enabled) {
349
+ console.log(chalk.green(`▶ Auto-start enabled for action: ${chalk.cyan(opts.action.trim())}`));
350
+ console.log(` The next sweep tick (≤5 min) will evaluate and fire this action`);
351
+ console.log(` if it is still ready, allow-listed, and has no live pipeline.`);
352
+ }
353
+ else {
354
+ console.log(chalk.dim(`No-op — auto-start was not disabled for action ${opts.action.trim()}.`));
355
+ }
356
+ }
357
+ catch (err) {
358
+ const msg = err instanceof Error ? err.message : String(err);
359
+ console.error(chalk.red(`Error: ${msg}`));
360
+ process.exit(1);
361
+ }
362
+ });
363
+ }
364
+ //# sourceMappingURL=autostart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autostart.js","sourceRoot":"","sources":["../src/autostart.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ import { registerBudgetCommands } from './budget.js';
21
21
  import { registerTriggerCommands } from './trigger.js';
22
22
  import { registerWakeCommands } from './wake.js';
23
23
  import { registerGovernanceCommands } from './governance.js';
24
+ import { registerAutoStartCommands } from './autostart.js';
24
25
  import { parseExecutionsJson, resolveWaitOptions, waitForPipelines, buildAwsCommand, validateExecutionEntry, } from './wait-for-pipeline.js';
25
26
  import { readFileSync, readdirSync, writeFileSync, mkdirSync, existsSync, statSync } from 'fs';
26
27
  import { fileURLToPath } from 'url';
@@ -2383,8 +2384,20 @@ ws
2383
2384
  .argument('<id>', 'Worksheet ID')
2384
2385
  .action(async (id) => {
2385
2386
  try {
2386
- const data = await api('GET', `/api/worksheets/${id}`);
2387
+ // G4 (AC15): fetch enriched actions in parallel so we can display
2388
+ // linked_pipeline status chips next to bound actions.
2389
+ const [data, actionsResult] = await Promise.all([
2390
+ api('GET', `/api/worksheets/${id}`),
2391
+ api('GET', `/api/worksheets/${id}/actions`).catch(() => null),
2392
+ ]);
2387
2393
  const w = data.worksheet;
2394
+ // Build a lookup map from action id → linked_pipeline (from the enriched endpoint)
2395
+ const actionPipelineMap = new Map();
2396
+ if (actionsResult?.actions) {
2397
+ for (const a of actionsResult.actions) {
2398
+ actionPipelineMap.set(a.id, a.linked_pipeline ?? null);
2399
+ }
2400
+ }
2388
2401
  console.log(chalk.cyan(w.name));
2389
2402
  console.log(` ID: ${w.id}`);
2390
2403
  console.log(` Purpose: ${w.purpose || '(none)'}`);
@@ -2414,7 +2427,16 @@ ws
2414
2427
  console.log(chalk.cyan(`\n Actions (${data.actions.length}):`));
2415
2428
  for (const a of data.actions) {
2416
2429
  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}`);
2430
+ // G4 (AC15): show pipeline status chip when action is bound to a pipeline
2431
+ const linkedPipeline = actionPipelineMap.get(a.id);
2432
+ let pipelineChip = '';
2433
+ if (linkedPipeline) {
2434
+ const chipColor = linkedPipeline.status === 'completed' ? chalk.green :
2435
+ linkedPipeline.status === 'failed' ? chalk.red :
2436
+ chalk.yellow;
2437
+ pipelineChip = ` ${chipColor(`[${linkedPipeline.status}]`)}`;
2438
+ }
2439
+ console.log(` ${chalk.cyan(a.description)}${progress}${pipelineChip} — ${a.member_name || 'unassigned'} — ${a.id}`);
2418
2440
  }
2419
2441
  }
2420
2442
  if (data.commitments?.length) {
@@ -4808,17 +4830,29 @@ pipelines
4808
4830
  .argument('<templateId>', 'Template ID')
4809
4831
  .requiredOption('-n, --name <name>', 'Instance name')
4810
4832
  .option('--params <json>', 'Parameters as JSON string')
4833
+ .option('--action <action-id>', 'Bind to a worksheet action at creation time (UUID)')
4811
4834
  .action(async (templateId, opts) => {
4812
4835
  try {
4813
4836
  const body = { templateId, name: opts.name };
4814
4837
  if (opts.params)
4815
4838
  body.parameters = parseJson(opts.params, '--params');
4839
+ // G4: optionally bind to a worksheet action at creation time (AC12)
4840
+ if (opts.action)
4841
+ body.worksheetActionId = opts.action;
4816
4842
  const data = await api('POST', '/api/pipeline-instances', body);
4817
4843
  const inst = data.instance;
4818
4844
  console.log(chalk.green(`Created: ${inst.name}`));
4819
4845
  console.log(` ID: ${inst.id}`);
4846
+ if (inst.worksheet_action_id) {
4847
+ console.log(` Bound Action: ${inst.worksheet_action_id}`);
4848
+ }
4820
4849
  }
4821
4850
  catch (err) {
4851
+ // G4: org-isolation guard fires when the action belongs to a different org (AC11)
4852
+ if (err instanceof ApiError && err.code === 'BINDING_ORG_ISOLATION_FAILED') {
4853
+ console.error(chalk.red('Binding failed: the action belongs to a different org than this pipeline.'));
4854
+ process.exit(1);
4855
+ }
4822
4856
  // F16 — Role-aware Connection-unavailable error (tech spec §5.14).
4823
4857
  // The pipeline-run path returns a structured payload when the
4824
4858
  // Project's Connection is missing/revoked/suspended/uninstalled.
@@ -4855,6 +4889,50 @@ pipelines
4855
4889
  process.exit(1);
4856
4890
  }
4857
4891
  });
4892
+ // G4 — Bind an existing pipeline instance to a worksheet action (AC13)
4893
+ pipelines
4894
+ .command('bind')
4895
+ .description('Bind an existing pipeline instance to a worksheet action')
4896
+ .argument('<instance-id>', 'Pipeline instance ID')
4897
+ .requiredOption('--action <action-id>', 'Worksheet action UUID to bind')
4898
+ .action(async (instanceId, opts) => {
4899
+ try {
4900
+ const data = await api('PATCH', `/api/pipeline-instances/${instanceId}`, {
4901
+ worksheetActionId: opts.action,
4902
+ });
4903
+ const inst = data.instance;
4904
+ console.log(chalk.green(`Bound: ${inst.name}`));
4905
+ console.log(` Pipeline: ${inst.id}`);
4906
+ console.log(` Action: ${inst.worksheet_action_id}`);
4907
+ }
4908
+ catch (err) {
4909
+ if (err instanceof ApiError && err.code === 'BINDING_ORG_ISOLATION_FAILED') {
4910
+ console.error(chalk.red('Binding failed: the action belongs to a different org than this pipeline.'));
4911
+ process.exit(1);
4912
+ }
4913
+ console.error(chalk.red(err.message));
4914
+ process.exit(1);
4915
+ }
4916
+ });
4917
+ // G4 — Remove binding between a pipeline instance and a worksheet action (AC14)
4918
+ pipelines
4919
+ .command('unbind')
4920
+ .description('Remove binding between a pipeline instance and a worksheet action')
4921
+ .argument('<instance-id>', 'Pipeline instance ID')
4922
+ .action(async (instanceId) => {
4923
+ try {
4924
+ const data = await api('PATCH', `/api/pipeline-instances/${instanceId}`, {
4925
+ worksheetActionId: null,
4926
+ });
4927
+ const inst = data.instance;
4928
+ console.log(chalk.green(`Unbound: ${inst.name}`));
4929
+ console.log(` Pipeline: ${inst.id}`);
4930
+ }
4931
+ catch (err) {
4932
+ console.error(chalk.red(err.message));
4933
+ process.exit(1);
4934
+ }
4935
+ });
4858
4936
  // I30 — `pl force-cancel` removed. Replaced by the generalised
4859
4937
  // `pl force-transition pipeline <id> --to cancelled` admin endpoint, which
4860
4938
  // covers the same emergency cancel path plus 12 more entity/state combos
@@ -5765,10 +5843,12 @@ program
5765
5843
  const executions = program.command('executions').alias('ex').description('Execution commands');
5766
5844
  executions
5767
5845
  .command('list')
5768
- .description('List executions')
5846
+ .description('List executions in the active project (or all projects with --all-projects)')
5769
5847
  .option('-s, --status <status>', 'Filter by status (pending, running, completed, failed)')
5770
5848
  .option('-a, --agent <name>', 'Filter by agent')
5771
5849
  .option('--source-type <type>', 'Filter by source type')
5850
+ .option('--project <slug>', 'Override active project for this call')
5851
+ .option('--all-projects', 'List executions across every project in the active org', false)
5772
5852
  .action(async (opts) => {
5773
5853
  try {
5774
5854
  const params = [];
@@ -5779,7 +5859,7 @@ executions
5779
5859
  if (opts.sourceType)
5780
5860
  params.push(`source_type=${opts.sourceType}`);
5781
5861
  const qs = params.length ? `?${params.join('&')}` : '';
5782
- const data = await api('GET', `/api/executions${qs}`);
5862
+ const data = await api('GET', `/api/executions${qs}`, undefined, projectOpts(opts));
5783
5863
  if (!data.executions?.length) {
5784
5864
  console.log(chalk.yellow('No executions'));
5785
5865
  return;
@@ -5790,12 +5870,20 @@ executions
5790
5870
  : e.status === 'failed' ? chalk.red('✗')
5791
5871
  : '○';
5792
5872
  const cost = e.cost_usd ? ` $${e.cost_usd}` : '';
5793
- console.log(` ${icon} ${e.agent_name}${e.source_type} (${e.status})${cost} ${e.id}`);
5873
+ // AUTON-PORTFOLIO-VIEWin the org-wide fleet view (--all-projects),
5874
+ // surface which project each execution belongs to so the list is
5875
+ // legible across projects. Degrades gracefully to project_id (the
5876
+ // executions list payload today includes project_id but not slug/name)
5877
+ // and is omitted entirely in the default single-project view where a
5878
+ // project column would be redundant.
5879
+ const projectCell = opts.allProjects
5880
+ ? chalk.gray(` [${e.project_slug || e.project_name || e.project_id || '—'}]`)
5881
+ : '';
5882
+ console.log(` ${icon} ${e.agent_name} — ${e.source_type} (${e.status})${cost}${projectCell} — ${e.id}`);
5794
5883
  }
5795
5884
  }
5796
5885
  catch (err) {
5797
- console.error(chalk.red(err.message));
5798
- process.exit(1);
5886
+ handleScopedError(err);
5799
5887
  }
5800
5888
  });
5801
5889
  executions
@@ -9432,6 +9520,13 @@ registerWakeCommands(program);
9432
9520
  // Cross-Org read (noProjectHeader: true); server resolves orgIds[] + roleSlugs[]
9433
9521
  // via resolveCallerContext. Read-only in v1 — no write sub-commands (AC1).
9434
9522
  registerGovernanceCommands(program);
9523
+ // ── AUTON-AUTOSTART — Action→Pipeline Auto-Start: autostart list/status/disable/enable ──
9524
+ // Registers `autostart` top-level command with list, status, disable, and enable
9525
+ // sub-commands. Project-scoped reads (project_id query param);
9526
+ // writes are org+project level (noProjectHeader: true on all calls).
9527
+ // admin/owner required for disable/enable; any active member for list/status.
9528
+ // No fire-now in V1 (spec §11 explicitly excludes it).
9529
+ registerAutoStartCommands(program);
9435
9530
  // Skip auto-parse only when running under Vitest (which sets process.env.VITEST).
9436
9531
  // Tests import `program` and drive it via `program.parseAsync(...)` after mocking
9437
9532
  // `./api.js`. In every other context — direct invocation, npx tsx, OR the bin