@formigio/fazemos-cli 0.10.55 → 0.10.59

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
@@ -26,6 +26,8 @@ import { registerFtrCommand } from './ftr.js';
26
26
  import { registerScheduleCommands } from './schedule.js';
27
27
  import { registerApprovalsCommands } from './approvals.js';
28
28
  import { registerSecretsCommands } from './commands/secrets.js';
29
+ import { registerProfilesCommands } from './commands/profiles.js';
30
+ import { registerImageCommands } from './commands/image.js';
29
31
  import { parseExecutionsJson, resolveWaitOptions, waitForPipelines, buildAwsCommand, validateExecutionEntry, } from './wait-for-pipeline.js';
30
32
  import { readFileSync, readdirSync, writeFileSync, mkdirSync, existsSync, statSync } from 'fs';
31
33
  import { fileURLToPath } from 'url';
@@ -6348,6 +6350,7 @@ program
6348
6350
  .option('--repos <repos>', 'Comma-separated repo names to clone (overrides agent config)', (v) => v.split(','))
6349
6351
  .option('--model <model>', 'Model override (e.g., opus, sonnet)')
6350
6352
  .option('--budget <usd>', 'Max budget override in USD', parseNumber)
6353
+ .option('--profile <name>', 'Named execution profile (F48-B). Threaded into POST /api/executions context as context.profile. Tracking-only in B.1 — does not change runtime resolution.')
6351
6354
  .action(async (sourceId, opts) => {
6352
6355
  try {
6353
6356
  const validTypes = ['action', 'commitment', 'pipeline_step'];
@@ -6480,6 +6483,13 @@ program
6480
6483
  }
6481
6484
  if (opts.prompt)
6482
6485
  context.prompt = opts.prompt;
6486
+ // [F48-B LD-14] --profile threads the named profile into context.profile
6487
+ // for tracking/dispatch. In Phase B.1 this field is recorded on the
6488
+ // executions row but does NOT drive runtime resolution — the nb-scanning
6489
+ // path continues through the unchanged Phase-A tools path. B.2 will wire
6490
+ // the DB-backed resolver at executionService.ts.
6491
+ if (opts.profile)
6492
+ context.profile = opts.profile;
6483
6493
  // [F24 §3.3 / D2] Forward --no-auto-complete intent. The CLI guard
6484
6494
  // above suppresses this for pipeline_step (forwardSuppress is false
6485
6495
  // in that case). Agent's buildSelfReportingInstructions reads
@@ -9767,7 +9777,7 @@ Recipients are resolved via the nearest .fazemos/roles.json registry, walking
9767
9777
  up from the current directory. Cross-workspace recipients are followed via
9768
9778
  the local registry's cross_workspace_roles block.
9769
9779
 
9770
- Types: question | task | signal | response | flag | decision | direction`)
9780
+ Types: question | task | signal | response | flag | decision | direction | brief`)
9771
9781
  .requiredOption('--from <role>', 'sender role-slug (required)')
9772
9782
  .option('--body <text>', 'markdown body of the dispatch (or use --body-file)')
9773
9783
  .option('--body-file <path>', 'read body from a file')
@@ -9783,7 +9793,8 @@ Types: question | task | signal | response | flag | decision | direction`)
9783
9793
  .action(async (to, type, opts) => {
9784
9794
  try {
9785
9795
  // Validate type
9786
- const allowedTypes = ['question', 'task', 'signal', 'response', 'flag', 'decision', 'direction'];
9796
+ // AUTON-ROLLUP-INBOX-API Leg B: 'brief' added (mirrors API allowedTypes + migration 068).
9797
+ const allowedTypes = ['question', 'task', 'signal', 'response', 'flag', 'decision', 'direction', 'brief'];
9787
9798
  if (!allowedTypes.includes(type)) {
9788
9799
  throw new Error(`Invalid type "${type}". Allowed: ${allowedTypes.join(', ')}`);
9789
9800
  }
@@ -10250,6 +10261,154 @@ program
10250
10261
  }
10251
10262
  }
10252
10263
  });
10264
+ // ── AUTON-ROLLUP-INBOX-API — `fazemos inbox` ─────────────────────────────────
10265
+ //
10266
+ // Drains dispatches from the DB via GET /api/dispatches.
10267
+ // Replaces the legacy file-read drain (roles/<slug>/inbox/*.md) that failed
10268
+ // cross-workspace (the root cause of the 2026-07-25 cadence incident).
10269
+ //
10270
+ // API mapping:
10271
+ // List: GET /api/dispatches?role=<slug>&status=<status>&limit=<N>
10272
+ // Mark-read: PATCH /api/dispatches/:id { status: "read" } × each item
10273
+ //
10274
+ // Auth: API-key (agent callers) or Cognito (human callers). Both are accepted
10275
+ // by requireAuth + the AUTON-ROLLUP-INBOX-API extension to GET / and PATCH /:id.
10276
+ program
10277
+ .command('inbox')
10278
+ .description(`Drain inbox dispatches from the API.
10279
+
10280
+ Reads dispatches from the DB via GET /api/dispatches and renders them as a
10281
+ table (default) or JSON. Use --mark-read to PATCH each returned dispatch to
10282
+ status=read after rendering (standard agent playbook usage).
10283
+
10284
+ Replaces the legacy file-read drain (roles/<slug>/inbox/*.md) that silently
10285
+ dropped cross-workspace dispatches.
10286
+
10287
+ Examples:
10288
+ fazemos inbox --role chief-of-staff # unread for role
10289
+ fazemos inbox --role engineering-director --status all
10290
+ fazemos inbox --role project-manager --mark-read # drain + mark read
10291
+ fazemos inbox --format json --role luca | jq .`)
10292
+ .option('--role <slug>', 'filter to a specific role-slug (defaults to all roles the caller fills)')
10293
+ .option('--status <status>', 'unread | read | all (default: unread)', 'unread')
10294
+ .option('--limit <n>', 'max dispatches to return (default: 50)', (v) => parseInt(v, 10), 50)
10295
+ .option('--format <format>', 'table | json (default: table)', 'table')
10296
+ .option('--mark-read', 'after listing, PATCH each dispatch to status=read')
10297
+ .action(async (opts) => {
10298
+ try {
10299
+ // Validate --status
10300
+ const validStatuses = ['unread', 'read', 'all'];
10301
+ if (!validStatuses.includes(opts.status)) {
10302
+ throw new Error(`Invalid --status "${opts.status}". Valid: ${validStatuses.join(', ')}`);
10303
+ }
10304
+ // Validate --format
10305
+ const validFormats = ['table', 'json'];
10306
+ if (!validFormats.includes(opts.format)) {
10307
+ throw new Error(`Invalid --format "${opts.format}". Valid: ${validFormats.join(', ')}`);
10308
+ }
10309
+ // Build query
10310
+ const params = new URLSearchParams();
10311
+ if (opts.role)
10312
+ params.set('role', opts.role);
10313
+ params.set('status', opts.status);
10314
+ params.set('limit', String(opts.limit));
10315
+ const result = await api('GET', `/api/dispatches?${params.toString()}`, undefined, {
10316
+ noProjectHeader: true,
10317
+ });
10318
+ const items = result.items ?? [];
10319
+ // ── JSON output ──────────────────────────────────────────
10320
+ if (opts.format === 'json') {
10321
+ console.log(JSON.stringify(result, null, 2));
10322
+ }
10323
+ else {
10324
+ // ── Table output ─────────────────────────────────────
10325
+ if (items.length === 0) {
10326
+ if (opts.role) {
10327
+ console.log(chalk.gray(`No ${opts.status} dispatches for role "${opts.role}".`));
10328
+ }
10329
+ else {
10330
+ console.log(chalk.gray(`No ${opts.status} dispatches.`));
10331
+ }
10332
+ }
10333
+ else {
10334
+ const now = Date.now();
10335
+ function age(iso) {
10336
+ const ms = now - new Date(iso).getTime();
10337
+ const mins = Math.floor(ms / 60000);
10338
+ if (mins < 60)
10339
+ return `${mins}m`;
10340
+ const hrs = Math.floor(mins / 60);
10341
+ if (hrs < 24)
10342
+ return `${hrs}h`;
10343
+ return `${Math.floor(hrs / 24)}d`;
10344
+ }
10345
+ // Column widths
10346
+ const COL_ROLE = 26;
10347
+ const COL_FROM = 22;
10348
+ const COL_TYPE = 10;
10349
+ const COL_PRI = 6;
10350
+ const COL_AGE = 5;
10351
+ const COL_BODY = 60;
10352
+ function pad(s, n) {
10353
+ return s.length > n ? s.slice(0, n - 1) + '…' : s.padEnd(n);
10354
+ }
10355
+ const header = [
10356
+ chalk.bold(pad('TO ROLE', COL_ROLE)),
10357
+ chalk.bold(pad('FROM', COL_FROM)),
10358
+ chalk.bold(pad('TYPE', COL_TYPE)),
10359
+ chalk.bold(pad('PRI', COL_PRI)),
10360
+ chalk.bold(pad('AGE', COL_AGE)),
10361
+ chalk.bold('SUMMARY'),
10362
+ ].join(' ');
10363
+ console.log(header);
10364
+ console.log(chalk.gray('─'.repeat(130)));
10365
+ for (const d of items) {
10366
+ const priColor = d.priority === 'high' ? chalk.red : d.priority === 'low' ? chalk.gray : chalk.white;
10367
+ const row = [
10368
+ pad(d.to_role ?? '', COL_ROLE),
10369
+ pad(d.from_role ?? '', COL_FROM),
10370
+ pad(d.type ?? '', COL_TYPE),
10371
+ priColor(pad(d.priority ?? 'normal', COL_PRI)),
10372
+ chalk.gray(pad(age(d.created_at), COL_AGE)),
10373
+ chalk.cyan(pad(d.summary ?? d.body?.split('\n')[0] ?? '', COL_BODY)),
10374
+ ].join(' ');
10375
+ console.log(row);
10376
+ }
10377
+ console.log(chalk.gray('─'.repeat(130)));
10378
+ console.log(chalk.gray(`${items.length} dispatch(es) shown.${result.nextCursor ? ' More available (use --limit to increase).' : ''}`));
10379
+ }
10380
+ }
10381
+ // ── --mark-read ──────────────────────────────────────────
10382
+ if (opts.markRead && items.length > 0) {
10383
+ const unread = items.filter(d => d.status === 'unread');
10384
+ if (unread.length === 0) {
10385
+ console.log(chalk.gray('\nAll shown dispatches already read; nothing to mark.'));
10386
+ }
10387
+ else {
10388
+ let marked = 0;
10389
+ let failed = 0;
10390
+ for (const d of unread) {
10391
+ try {
10392
+ await api('PATCH', `/api/dispatches/${d.id}`, { status: 'read' }, {
10393
+ noProjectHeader: true,
10394
+ });
10395
+ marked++;
10396
+ }
10397
+ catch (err) {
10398
+ failed++;
10399
+ const msg = err instanceof Error ? err.message : String(err);
10400
+ console.error(chalk.yellow(` ⚠ Failed to mark dispatch ${d.id} as read: ${msg}`));
10401
+ }
10402
+ }
10403
+ console.log(chalk.green(`\n✓ Marked ${marked} dispatch(es) as read.${failed > 0 ? chalk.yellow(` ${failed} failed.`) : ''}`));
10404
+ }
10405
+ }
10406
+ }
10407
+ catch (err) {
10408
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
10409
+ process.exit(1);
10410
+ }
10411
+ });
10253
10412
  // ── F32 — Pause Switches: pause / resume / pause status ─────────────────────
10254
10413
  // Mirrors the dispatch group pattern. Registers `pause`, `resume` top-level
10255
10414
  // commands and the `pause status` sub-command. All calls are org-level
@@ -10315,6 +10474,22 @@ registerApprovalsCommands(program);
10315
10474
  // set supports interactive masked input, --value-from <file>, positional arg,
10316
10475
  // and piped stdin. delete prompts for confirmation unless --yes / -y is passed.
10317
10476
  registerSecretsCommands(program);
10477
+ // ── F48-B — Execution Profiles: profiles create / list / show / delete / register-source ────
10478
+ // Registers `profiles` top-level command and sub-commands.
10479
+ // All calls use project ID in the URL path (/api/projects/:projectId/profiles/...);
10480
+ // noProjectHeader: true on every call (path-scoped, not header-scoped).
10481
+ // create/delete/register-source: owner/admin only (server-side gate).
10482
+ // list/show: all project members.
10483
+ // register-image / admit: B2 DEFERRED stubs (print friendly error + exit 1).
10484
+ registerProfilesCommands(program);
10485
+ // ── F48-B — Image Builds: image build / status / list ───────────────────────
10486
+ // Registers `image` top-level command with build, status, and list sub-commands.
10487
+ // All calls use project ID in the URL path (/api/projects/:projectId/profiles/:name/builds[/:id]);
10488
+ // noProjectHeader: true on every call.
10489
+ // build: owner/admin only. status/list: all project members.
10490
+ // When codebuildEnabled=false on a build response, the note field is surfaced
10491
+ // to the user (infrastructure not yet deployed).
10492
+ registerImageCommands(program);
10318
10493
  // Skip auto-parse only when running under Vitest (which sets process.env.VITEST).
10319
10494
  // Tests import `program` and drive it via `program.parseAsync(...)` after mocking
10320
10495
  // `./api.js`. In every other context — direct invocation, npx tsx, OR the bin