@formigio/fazemos-cli 0.10.67 → 0.10.74
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/commands/preflight.d.ts +41 -0
- package/dist/commands/preflight.js +706 -0
- package/dist/commands/preflight.js.map +1 -0
- package/dist/commands/provision.d.ts +29 -0
- package/dist/commands/provision.js +382 -0
- package/dist/commands/provision.js.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -1
- package/dist/preflight/repoRead.d.ts +65 -0
- package/dist/preflight/repoRead.js +194 -0
- package/dist/preflight/repoRead.js.map +1 -0
- package/dist/preflight/roster.d.ts +65 -0
- package/dist/preflight/roster.js +153 -0
- package/dist/preflight/roster.js.map +1 -0
- package/dist/preflight/row1_member.d.ts +8 -0
- package/dist/preflight/row1_member.js +64 -0
- package/dist/preflight/row1_member.js.map +1 -0
- package/dist/preflight/row2_roleRegistration.d.ts +18 -0
- package/dist/preflight/row2_roleRegistration.js +156 -0
- package/dist/preflight/row2_roleRegistration.js.map +1 -0
- package/dist/preflight/row3_persona.d.ts +24 -0
- package/dist/preflight/row3_persona.js +92 -0
- package/dist/preflight/row3_persona.js.map +1 -0
- package/dist/preflight/row3_playbook.d.ts +30 -0
- package/dist/preflight/row3_playbook.js +142 -0
- package/dist/preflight/row3_playbook.js.map +1 -0
- package/dist/preflight/row3_rolesJson.d.ts +12 -0
- package/dist/preflight/row3_rolesJson.js +38 -0
- package/dist/preflight/row3_rolesJson.js.map +1 -0
- package/dist/preflight/row3_workspace.d.ts +24 -0
- package/dist/preflight/row3_workspace.js +253 -0
- package/dist/preflight/row3_workspace.js.map +1 -0
- package/dist/preflight/row4_ssm.d.ts +57 -0
- package/dist/preflight/row4_ssm.js +235 -0
- package/dist/preflight/row4_ssm.js.map +1 -0
- package/dist/preflight/row5_workerLambdaWakeConfig.d.ts +20 -0
- package/dist/preflight/row5_workerLambdaWakeConfig.js +113 -0
- package/dist/preflight/row5_workerLambdaWakeConfig.js.map +1 -0
- package/dist/preflight/row6_ingest.d.ts +20 -0
- package/dist/preflight/row6_ingest.js +178 -0
- package/dist/preflight/row6_ingest.js.map +1 -0
- package/dist/preflight/schema.d.ts +175 -0
- package/dist/preflight/schema.js +62 -0
- package/dist/preflight/schema.js.map +1 -0
- package/dist/preflight/workerLambdas.d.ts +22 -0
- package/dist/preflight/workerLambdas.js +25 -0
- package/dist/preflight/workerLambdas.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Rows 3a/3b/3c — Workspace repo file checks.
|
|
3
|
+
*
|
|
4
|
+
* Checks performed via `gh api` against origin/main (D3: remote, not local copy).
|
|
5
|
+
* 3a — persona file exists (path from role.persona in roles.json)
|
|
6
|
+
* 3b — playbook file exists (derived from role.inbox path convention)
|
|
7
|
+
* 3c — .fazemos/roles.json has the role entry with a filler (local check)
|
|
8
|
+
*
|
|
9
|
+
* Row 3 rows do NOT run if `gh` is unavailable (whole group becomes tooling_error).
|
|
10
|
+
*/
|
|
11
|
+
import { checkGhAvailable, getWorkspaceGitHubRepo, getFileContentOnGitHub, listDirectoryFilenamesOnGitHub, ToolingError, } from './repoRead.js';
|
|
12
|
+
/**
|
|
13
|
+
* Strip YAML frontmatter from a markdown file's content.
|
|
14
|
+
* Frontmatter is a `---` block at the start of the file.
|
|
15
|
+
* Returns only the body — the part after the closing `---` line.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors the API-side guard in fazemos-api/src/routes/members.ts (fileDoc.body?.trim())
|
|
18
|
+
* which emits a structured `role_doc_empty_body` warn on frontmatter-only files.
|
|
19
|
+
*/
|
|
20
|
+
function stripYamlFrontmatter(text) {
|
|
21
|
+
const match = text.match(/^---\r?\n[\s\S]*?\n---\r?\n?/);
|
|
22
|
+
return match ? text.slice(match[0].length) : text;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Derive the role's directory path from its inbox path.
|
|
26
|
+
* e.g. "projects/fazemos/roles/project-manager/inbox/" → "projects/fazemos/roles/project-manager"
|
|
27
|
+
* Falls back to "roles/<role_slug>" if inbox is not set.
|
|
28
|
+
*/
|
|
29
|
+
function derivRoleDir(roleEntry, roleSlug) {
|
|
30
|
+
if (roleEntry?.inbox) {
|
|
31
|
+
// Strip trailing slash and the 'inbox' segment
|
|
32
|
+
const trimmed = roleEntry.inbox.replace(/\/+$/, '');
|
|
33
|
+
const parts = trimmed.split('/');
|
|
34
|
+
if (parts[parts.length - 1] === 'inbox') {
|
|
35
|
+
return parts.slice(0, -1).join('/');
|
|
36
|
+
}
|
|
37
|
+
return trimmed;
|
|
38
|
+
}
|
|
39
|
+
return `roles/${roleSlug}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run rows 3a, 3b, 3c for a given workspace/role.
|
|
43
|
+
*/
|
|
44
|
+
export async function checkRow3Workspace(opts) {
|
|
45
|
+
const { workspaceRoot, roleSlug, agentName, roleEntry, registry } = opts;
|
|
46
|
+
// ── Check gh CLI availability first ──────────────────────────────────────
|
|
47
|
+
let ghAvailable = true;
|
|
48
|
+
let ghError = '';
|
|
49
|
+
try {
|
|
50
|
+
checkGhAvailable();
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
ghAvailable = false;
|
|
54
|
+
ghError = err instanceof ToolingError ? err.message : String(err);
|
|
55
|
+
}
|
|
56
|
+
const repoRef = ghAvailable ? getWorkspaceGitHubRepo(workspaceRoot) : null;
|
|
57
|
+
if (!ghAvailable || !repoRef) {
|
|
58
|
+
const detail = !ghAvailable
|
|
59
|
+
? ghError
|
|
60
|
+
: 'Cannot determine GitHub repo from workspace git remote.origin.url.';
|
|
61
|
+
const row3tooling = {
|
|
62
|
+
id: '3a',
|
|
63
|
+
name: 'persona file (GitHub)',
|
|
64
|
+
status: 'tooling_error',
|
|
65
|
+
detail,
|
|
66
|
+
fix_command: null,
|
|
67
|
+
fix_instruction: 'Install and authenticate gh CLI: brew install gh && gh auth login',
|
|
68
|
+
};
|
|
69
|
+
return [
|
|
70
|
+
row3tooling,
|
|
71
|
+
{ ...row3tooling, id: '3b', name: 'playbook file (GitHub)' },
|
|
72
|
+
{
|
|
73
|
+
id: '3c',
|
|
74
|
+
name: 'roles.json entry',
|
|
75
|
+
status: 'tooling_error',
|
|
76
|
+
detail,
|
|
77
|
+
fix_command: null,
|
|
78
|
+
fix_instruction: 'gh CLI required only for 3a/3b; 3c uses local check but grouped here for clarity.',
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
const roleDir = derivRoleDir(roleEntry, roleSlug);
|
|
83
|
+
// ── Row 3a resolution note ───────────────────────────────────────────────
|
|
84
|
+
// The runtime (F18 file resolution) reads docs/agents/<name>.md from the
|
|
85
|
+
// workspace repo (org_file layer). The roles.json `persona` field points to
|
|
86
|
+
// a different path in many workspaces — that field is NOT what the runtime
|
|
87
|
+
// reads. We check the actual runtime path, not the roles.json value.
|
|
88
|
+
const runtimePersonaPath = `docs/agents/${agentName}.md`;
|
|
89
|
+
const rows = [];
|
|
90
|
+
// ── 3a — persona file (runtime resolution path: docs/agents/<name>.md) ──
|
|
91
|
+
// Checks the *body* after YAML frontmatter, not just byte size. A BUG160 stub
|
|
92
|
+
// carries frontmatter (~60–80 bytes) with an empty body — `size > 0` would
|
|
93
|
+
// PASS it. We mirror the API-side guard (fazemos-api/src/routes/members.ts:150,
|
|
94
|
+
// `fileDoc.body?.trim()`) and test the actual body content.
|
|
95
|
+
//
|
|
96
|
+
// Cost note: this is a content fetch via the GitHub Contents API rather than a
|
|
97
|
+
// metadata-only call. For typical persona files (small markdown docs) the extra
|
|
98
|
+
// payload is negligible; the Content API endpoint is the same either way.
|
|
99
|
+
try {
|
|
100
|
+
const content = getFileContentOnGitHub(repoRef, runtimePersonaPath);
|
|
101
|
+
const exists = content !== null;
|
|
102
|
+
// Strip YAML frontmatter and test trimmed body — frontmatter-only is empty
|
|
103
|
+
const body = exists ? stripYamlFrontmatter(content) : '';
|
|
104
|
+
const nonEmpty = exists && body.trim().length > 0;
|
|
105
|
+
// Note when roles.json persona field diverges from the runtime resolution path
|
|
106
|
+
const roleJsonPersona = roleEntry?.persona;
|
|
107
|
+
const driftNote = roleJsonPersona && roleJsonPersona !== runtimePersonaPath
|
|
108
|
+
? ` (roles.json persona='${roleJsonPersona}' — that field is not what the runtime reads; ` +
|
|
109
|
+
`see docs/guides/agent-provisioning-runbook.md §3)`
|
|
110
|
+
: '';
|
|
111
|
+
if (!exists) {
|
|
112
|
+
rows.push({
|
|
113
|
+
id: '3a',
|
|
114
|
+
name: 'persona file',
|
|
115
|
+
status: 'fail',
|
|
116
|
+
detail: `${runtimePersonaPath} not found on ${repoRef.owner}/${repoRef.repo}@${repoRef.ref}.` +
|
|
117
|
+
driftNote,
|
|
118
|
+
fix_command: null,
|
|
119
|
+
fix_instruction: `Create the agent persona at ${runtimePersonaPath} in the workspace repo. ` +
|
|
120
|
+
`The F18 runtime reads this path — not the 'persona' field in .fazemos/roles.json. ` +
|
|
121
|
+
`Reference another docs/agents/*.md file as a pattern.`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else if (!nonEmpty) {
|
|
125
|
+
rows.push({
|
|
126
|
+
id: '3a',
|
|
127
|
+
name: 'persona file',
|
|
128
|
+
status: 'fail',
|
|
129
|
+
detail: `${runtimePersonaPath} exists but has no body content (frontmatter-only or blank) on ` +
|
|
130
|
+
`${repoRef.owner}/${repoRef.repo}@${repoRef.ref}. ` +
|
|
131
|
+
`A frontmatter-only file replaces the DB systemPrompt with "" at runtime (BUG160 class).` +
|
|
132
|
+
driftNote,
|
|
133
|
+
fix_command: null,
|
|
134
|
+
fix_instruction: `Populate ${runtimePersonaPath} with the agent's full persona text below the YAML frontmatter. ` +
|
|
135
|
+
`Do NOT leave the body empty — the runtime substitutes the file body for the DB systemPrompt; ` +
|
|
136
|
+
`frontmatter-only silences the agent's identity. Reference another docs/agents/*.md as a pattern.`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
rows.push({
|
|
141
|
+
id: '3a',
|
|
142
|
+
name: 'persona file',
|
|
143
|
+
status: 'pass',
|
|
144
|
+
detail: `${runtimePersonaPath} exists with non-empty body on ` +
|
|
145
|
+
`${repoRef.owner}/${repoRef.repo}@${repoRef.ref}.` +
|
|
146
|
+
driftNote,
|
|
147
|
+
fix_command: null,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
const detail = err instanceof ToolingError ? err.message : String(err);
|
|
153
|
+
rows.push({
|
|
154
|
+
id: '3a',
|
|
155
|
+
name: 'persona file',
|
|
156
|
+
status: 'tooling_error',
|
|
157
|
+
detail,
|
|
158
|
+
fix_command: null,
|
|
159
|
+
fix_instruction: 'gh CLI authentication or network issue. Run: gh auth login',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// ── 3b — playbook file (any playbook-*.md in role dir) ──────────────────
|
|
163
|
+
// PASS if any playbook-*.md exists. Roles carry different cadences
|
|
164
|
+
// (playbook-daily, playbook-weekly, playbook-task, etc.) — do not hard-code
|
|
165
|
+
// a single expected filename.
|
|
166
|
+
try {
|
|
167
|
+
const dirFiles = listDirectoryFilenamesOnGitHub(repoRef, roleDir);
|
|
168
|
+
const playbookFiles = (dirFiles ?? []).filter((f) => /^playbook-.+\.md$/.test(f));
|
|
169
|
+
if (!dirFiles) {
|
|
170
|
+
rows.push({
|
|
171
|
+
id: '3b',
|
|
172
|
+
name: 'playbook file',
|
|
173
|
+
status: 'fail',
|
|
174
|
+
detail: `Role directory '${roleDir}/' not found on ${repoRef.owner}/${repoRef.repo}@${repoRef.ref}. ` +
|
|
175
|
+
`No playbook files can exist.`,
|
|
176
|
+
fix_command: null,
|
|
177
|
+
fix_instruction: `Create the role directory and at least one playbook file ` +
|
|
178
|
+
`(e.g. ${roleDir}/playbook-daily.md or ${roleDir}/playbook-task.md) in the workspace repo. ` +
|
|
179
|
+
`See docs/guides/agent-provisioning-runbook.md §3.`,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else if (playbookFiles.length === 0) {
|
|
183
|
+
const mdFiles = dirFiles.filter((f) => f.endsWith('.md'));
|
|
184
|
+
rows.push({
|
|
185
|
+
id: '3b',
|
|
186
|
+
name: 'playbook file',
|
|
187
|
+
status: 'fail',
|
|
188
|
+
detail: `No playbook-*.md files found in ${roleDir}/ on ${repoRef.owner}/${repoRef.repo}@${repoRef.ref}. ` +
|
|
189
|
+
`Found .md files: ${mdFiles.length > 0 ? mdFiles.join(', ') : 'none'}.`,
|
|
190
|
+
fix_command: null,
|
|
191
|
+
fix_instruction: `Create at least one playbook file in ${roleDir}/ (e.g. playbook-daily.md or playbook-task.md). ` +
|
|
192
|
+
`Reference another role's playbooks as a pattern ` +
|
|
193
|
+
`(e.g. look at other roles in projects/fazemos/roles/ for examples). ` +
|
|
194
|
+
`See docs/guides/agent-provisioning-runbook.md §3.`,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
rows.push({
|
|
199
|
+
id: '3b',
|
|
200
|
+
name: 'playbook file',
|
|
201
|
+
status: 'pass',
|
|
202
|
+
detail: `Found ${playbookFiles.length} playbook file(s) in ${roleDir}/: ${playbookFiles.join(', ')}`,
|
|
203
|
+
fix_command: null,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
const detail = err instanceof ToolingError ? err.message : String(err);
|
|
209
|
+
rows.push({
|
|
210
|
+
id: '3b',
|
|
211
|
+
name: 'playbook file',
|
|
212
|
+
status: 'tooling_error',
|
|
213
|
+
detail,
|
|
214
|
+
fix_command: null,
|
|
215
|
+
fix_instruction: 'gh CLI authentication or network issue. Run: gh auth login',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// ── 3c — roles.json entry (local check) ──────────────────────────────────
|
|
219
|
+
if (!registry || !registry.roles[roleSlug]) {
|
|
220
|
+
rows.push({
|
|
221
|
+
id: '3c',
|
|
222
|
+
name: 'roles.json entry',
|
|
223
|
+
status: 'fail',
|
|
224
|
+
detail: `Role '${roleSlug}' not found in .fazemos/roles.json (or no registry found).`,
|
|
225
|
+
fix_command: null,
|
|
226
|
+
fix_instruction: `Add a filler entry for '${roleSlug}' in the workspace .fazemos/roles.json, ` +
|
|
227
|
+
`then run: fazemos roles sync`,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
else if (!registry.roles[roleSlug].filler?.identity) {
|
|
231
|
+
rows.push({
|
|
232
|
+
id: '3c',
|
|
233
|
+
name: 'roles.json entry',
|
|
234
|
+
status: 'fail',
|
|
235
|
+
detail: `Role '${roleSlug}' in .fazemos/roles.json has no filler.identity.`,
|
|
236
|
+
fix_command: null,
|
|
237
|
+
fix_instruction: `Set filler.identity for '${roleSlug}' in .fazemos/roles.json (the agent's display name), ` +
|
|
238
|
+
`then run: fazemos roles sync`,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
const entry = registry.roles[roleSlug];
|
|
243
|
+
rows.push({
|
|
244
|
+
id: '3c',
|
|
245
|
+
name: 'roles.json entry',
|
|
246
|
+
status: 'pass',
|
|
247
|
+
detail: `${roleSlug}: filler.type=${entry.filler.type}, filler.identity=${entry.filler.identity}, scope=${entry.scope}`,
|
|
248
|
+
fix_command: null,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return rows;
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=row3_workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row3_workspace.js","sourceRoot":"","sources":["../../src/preflight/row3_workspace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Rows 4a/4b/4c — SSM allowlist checks.
|
|
3
|
+
*
|
|
4
|
+
* Checks that the role appears in the three Fazemos SSM parameters:
|
|
5
|
+
* 4a — /fazemos/<env>/wake/allowlist (comma-separated role slugs)
|
|
6
|
+
* 4b — /fazemos/<env>/wake/auto-wakeable-roles (comma-separated role slugs)
|
|
7
|
+
* 4c — /fazemos/<env>/triggers/allowlist (role:cadence pairs, e.g. "project-manager:daily")
|
|
8
|
+
*
|
|
9
|
+
* All three rows look up the **role slug** — NOT the agent/filler name.
|
|
10
|
+
* Row 4c entries use a `role:cadence` format; only the role portion is compared,
|
|
11
|
+
* and the cadence is surfaced in the PASS detail for operator visibility.
|
|
12
|
+
*
|
|
13
|
+
* Uses the `aws ssm get-parameter` CLI (consistent with existing shell-out pattern
|
|
14
|
+
* in index.ts for wait-for-pipeline). Operator credentials required.
|
|
15
|
+
*
|
|
16
|
+
* On AWS credential failure → tooling_error with fix instruction (aws sso login).
|
|
17
|
+
* On access denied → tooling_error.
|
|
18
|
+
* On parameter not found → fail with fix command (aws ssm put-parameter ...).
|
|
19
|
+
*
|
|
20
|
+
* `triggersAllowlist` returns bare role slugs (`:cadence` suffix stripped) so that
|
|
21
|
+
* row3_playbook.ts's `triggersAllowlist.includes(roleSlug)` check works correctly.
|
|
22
|
+
*
|
|
23
|
+
* r3.3 — Row 4c polarity fix (2026-08-20):
|
|
24
|
+
* Row 4c is now symmetric with row 3b. Previously it FAILed any role absent from the
|
|
25
|
+
* triggers allowlist. Now it classifies by cadence-playbook presence (mirroring how
|
|
26
|
+
* row 3b classifies by allowlist presence):
|
|
27
|
+
*
|
|
28
|
+
* Role present in triggers allowlist → PASS (cadence named in detail)
|
|
29
|
+
* Absent from allowlist + cadence playbook → FAIL (real gap)
|
|
30
|
+
* Absent from allowlist + no cadence playbook → SKIP (dispatch-driven by design)
|
|
31
|
+
* Triggers allowlist unreadable → TOOLING-ERROR
|
|
32
|
+
*
|
|
33
|
+
* `playbookFiles` is pre-fetched by the caller (commands/preflight.ts) and passed in,
|
|
34
|
+
* the same way row 3b receives the triggers allowlist from row 4c's fetch.
|
|
35
|
+
*/
|
|
36
|
+
import type { PreflightRow } from './schema.js';
|
|
37
|
+
export declare function checkRow4Ssm(opts: {
|
|
38
|
+
env: string;
|
|
39
|
+
/** Agent display name — retained for API compat; allowlist checks use roleSlug. */
|
|
40
|
+
agentName: string;
|
|
41
|
+
/** Role slug checked in all three SSM parameters. */
|
|
42
|
+
roleSlug: string;
|
|
43
|
+
region?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Playbook filenames in the role's workspace directory, pre-fetched by the caller.
|
|
46
|
+
* Used by row 4c to classify SKIP vs FAIL when the role is absent from the triggers
|
|
47
|
+
* allowlist — symmetric with row 3b receiving the triggers allowlist from row 4c.
|
|
48
|
+
*
|
|
49
|
+
* `string[]` — list of playbook-*.md files; empty means no playbooks at all.
|
|
50
|
+
* `'unavailable'` — gh CLI not available / read failed; row 4c conservatively FAILs.
|
|
51
|
+
* `undefined` — not provided; row 4c conservatively FAILs (same as unavailable).
|
|
52
|
+
*/
|
|
53
|
+
playbookFiles?: string[] | 'unavailable';
|
|
54
|
+
}): Promise<{
|
|
55
|
+
rows: PreflightRow[];
|
|
56
|
+
triggersAllowlist: string[] | 'unavailable';
|
|
57
|
+
}>;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Rows 4a/4b/4c — SSM allowlist checks.
|
|
3
|
+
*
|
|
4
|
+
* Checks that the role appears in the three Fazemos SSM parameters:
|
|
5
|
+
* 4a — /fazemos/<env>/wake/allowlist (comma-separated role slugs)
|
|
6
|
+
* 4b — /fazemos/<env>/wake/auto-wakeable-roles (comma-separated role slugs)
|
|
7
|
+
* 4c — /fazemos/<env>/triggers/allowlist (role:cadence pairs, e.g. "project-manager:daily")
|
|
8
|
+
*
|
|
9
|
+
* All three rows look up the **role slug** — NOT the agent/filler name.
|
|
10
|
+
* Row 4c entries use a `role:cadence` format; only the role portion is compared,
|
|
11
|
+
* and the cadence is surfaced in the PASS detail for operator visibility.
|
|
12
|
+
*
|
|
13
|
+
* Uses the `aws ssm get-parameter` CLI (consistent with existing shell-out pattern
|
|
14
|
+
* in index.ts for wait-for-pipeline). Operator credentials required.
|
|
15
|
+
*
|
|
16
|
+
* On AWS credential failure → tooling_error with fix instruction (aws sso login).
|
|
17
|
+
* On access denied → tooling_error.
|
|
18
|
+
* On parameter not found → fail with fix command (aws ssm put-parameter ...).
|
|
19
|
+
*
|
|
20
|
+
* `triggersAllowlist` returns bare role slugs (`:cadence` suffix stripped) so that
|
|
21
|
+
* row3_playbook.ts's `triggersAllowlist.includes(roleSlug)` check works correctly.
|
|
22
|
+
*
|
|
23
|
+
* r3.3 — Row 4c polarity fix (2026-08-20):
|
|
24
|
+
* Row 4c is now symmetric with row 3b. Previously it FAILed any role absent from the
|
|
25
|
+
* triggers allowlist. Now it classifies by cadence-playbook presence (mirroring how
|
|
26
|
+
* row 3b classifies by allowlist presence):
|
|
27
|
+
*
|
|
28
|
+
* Role present in triggers allowlist → PASS (cadence named in detail)
|
|
29
|
+
* Absent from allowlist + cadence playbook → FAIL (real gap)
|
|
30
|
+
* Absent from allowlist + no cadence playbook → SKIP (dispatch-driven by design)
|
|
31
|
+
* Triggers allowlist unreadable → TOOLING-ERROR
|
|
32
|
+
*
|
|
33
|
+
* `playbookFiles` is pre-fetched by the caller (commands/preflight.ts) and passed in,
|
|
34
|
+
* the same way row 3b receives the triggers allowlist from row 4c's fetch.
|
|
35
|
+
*/
|
|
36
|
+
import { execSync } from 'child_process';
|
|
37
|
+
/**
|
|
38
|
+
* Determine whether the role has a cadence (daily/weekly) playbook.
|
|
39
|
+
*
|
|
40
|
+
* Returns `true` if the list contains a playbook-daily.md or playbook-weekly.md,
|
|
41
|
+
* or if the list is `'unavailable'` / `undefined` (conservative — treat unknown
|
|
42
|
+
* as potentially cadence-bearing so we FAIL rather than silently SKIP).
|
|
43
|
+
*
|
|
44
|
+
* Returns `false` when the caller confirms no cadence playbook exists (empty array
|
|
45
|
+
* or array containing only task/other playbooks), enabling row 4c to SKIP.
|
|
46
|
+
*/
|
|
47
|
+
function hasCadencePlaybookFile(playbookFiles) {
|
|
48
|
+
if (playbookFiles === 'unavailable' || playbookFiles === undefined) {
|
|
49
|
+
// Conservative: cannot determine playbook state → treat as requiring a trigger.
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
const cadencePattern = /^playbook-(daily|weekly)\.md$/;
|
|
53
|
+
return playbookFiles.some((f) => cadencePattern.test(f));
|
|
54
|
+
}
|
|
55
|
+
function runSsmGetParameter(paramName, region) {
|
|
56
|
+
const regionFlag = region ? `--region ${region}` : '';
|
|
57
|
+
try {
|
|
58
|
+
const output = execSync(`aws ssm get-parameter --name "${paramName}" --query Parameter.Value --output text ${regionFlag}`.trim(), { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
59
|
+
return output;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const stderr = String(err.stderr ?? '');
|
|
63
|
+
if (stderr.includes('ParameterNotFound') || stderr.includes('parameter not found')) {
|
|
64
|
+
return null; // Parameter doesn't exist
|
|
65
|
+
}
|
|
66
|
+
throw err; // Re-throw for credential / access errors
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function parseAllowList(value) {
|
|
70
|
+
return value
|
|
71
|
+
.split(',')
|
|
72
|
+
.map((s) => s.trim())
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check whether `needle` is present in `list`.
|
|
77
|
+
*
|
|
78
|
+
* For row 4c (role:cadence format), pass `entryKeyExtractor: (e) => e.split(':')[0]`
|
|
79
|
+
* so that "project-manager:daily" matches needle "project-manager".
|
|
80
|
+
*
|
|
81
|
+
* Returns `{ found, matchedEntry }` so callers can surface the full entry
|
|
82
|
+
* (e.g. "project-manager:daily") in PASS detail output.
|
|
83
|
+
*/
|
|
84
|
+
function checkMembership(list, needle, entryKeyExtractor) {
|
|
85
|
+
const normNeedle = needle.toLowerCase().replace(/[-_\s]+/g, '-');
|
|
86
|
+
for (const item of list) {
|
|
87
|
+
const key = entryKeyExtractor ? entryKeyExtractor(item) : item;
|
|
88
|
+
const normKey = key.toLowerCase().replace(/[-_\s]+/g, '-');
|
|
89
|
+
if (normKey === normNeedle) {
|
|
90
|
+
return { found: true, matchedEntry: item };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { found: false };
|
|
94
|
+
}
|
|
95
|
+
export async function checkRow4Ssm(opts) {
|
|
96
|
+
const { env, roleSlug, region } = opts;
|
|
97
|
+
let triggersAllowlistValue = 'unavailable';
|
|
98
|
+
const checks = [
|
|
99
|
+
{
|
|
100
|
+
id: '4a',
|
|
101
|
+
name: 'SSM wake allowlist',
|
|
102
|
+
paramPath: `/fazemos/${env}/wake/allowlist`,
|
|
103
|
+
// FIX: parameter holds role slugs, not agent names (matches row 4b pattern).
|
|
104
|
+
needle: roleSlug,
|
|
105
|
+
needleLabel: `role '${roleSlug}'`,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: '4b',
|
|
109
|
+
name: 'SSM auto-wakeable-roles allowlist',
|
|
110
|
+
paramPath: `/fazemos/${env}/wake/auto-wakeable-roles`,
|
|
111
|
+
needle: roleSlug,
|
|
112
|
+
needleLabel: `role '${roleSlug}'`,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: '4c',
|
|
116
|
+
name: 'SSM triggers allowlist',
|
|
117
|
+
paramPath: `/fazemos/${env}/triggers/allowlist`,
|
|
118
|
+
needle: roleSlug,
|
|
119
|
+
needleLabel: `role '${roleSlug}'`,
|
|
120
|
+
// FIX: entries are "role:cadence" — compare only the role portion before the colon.
|
|
121
|
+
entryKeyExtractor: (entry) => entry.split(':')[0],
|
|
122
|
+
// When appending a new entry, include the default cadence suffix.
|
|
123
|
+
newEntryValue: `${roleSlug}:daily`,
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
const results = [];
|
|
127
|
+
for (const check of checks) {
|
|
128
|
+
let paramValue;
|
|
129
|
+
try {
|
|
130
|
+
paramValue = runSsmGetParameter(check.paramPath, region);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
const stderr = String(err.stderr ?? err.message ?? '');
|
|
134
|
+
const isCredentialError = stderr.includes('Unable to locate credentials') ||
|
|
135
|
+
stderr.includes('ExpiredToken') ||
|
|
136
|
+
stderr.includes('NoCredentialProviders') ||
|
|
137
|
+
stderr.includes('AccessDenied') ||
|
|
138
|
+
stderr.includes('not authorized');
|
|
139
|
+
results.push({
|
|
140
|
+
id: check.id,
|
|
141
|
+
name: check.name,
|
|
142
|
+
status: 'tooling_error',
|
|
143
|
+
detail: isCredentialError
|
|
144
|
+
? `AWS credentials unavailable or expired: ${stderr.split('\n')[0].trim()}`
|
|
145
|
+
: `aws ssm get-parameter failed: ${stderr.split('\n')[0].trim()}`,
|
|
146
|
+
fix_command: null,
|
|
147
|
+
fix_instruction: 'Run: aws sso login --profile AdministratorAccess-047982206554',
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (paramValue === null) {
|
|
152
|
+
// Parameter does not exist at all.
|
|
153
|
+
// Capture empty list for row3b (no entries = no cadence-bearing roles configured).
|
|
154
|
+
if (check.id === '4c') {
|
|
155
|
+
triggersAllowlistValue = [];
|
|
156
|
+
// r3.3: SKIP if dispatch-driven (no cadence playbook); FAIL only if a cadence
|
|
157
|
+
// playbook exists that would fire with no trigger entry.
|
|
158
|
+
if (!hasCadencePlaybookFile(opts.playbookFiles)) {
|
|
159
|
+
results.push({
|
|
160
|
+
id: check.id,
|
|
161
|
+
name: check.name,
|
|
162
|
+
status: 'skip',
|
|
163
|
+
detail: `Parameter ${check.paramPath} does not exist — ` +
|
|
164
|
+
`role '${check.needle}' is dispatch-driven; cadence trigger not required by design.`,
|
|
165
|
+
fix_command: null,
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const valueToAdd = check.newEntryValue ?? check.needle;
|
|
171
|
+
results.push({
|
|
172
|
+
id: check.id,
|
|
173
|
+
name: check.name,
|
|
174
|
+
status: 'fail',
|
|
175
|
+
detail: `Parameter ${check.paramPath} does not exist.`,
|
|
176
|
+
fix_command: `aws ssm put-parameter --name "${check.paramPath}" --value "${valueToAdd}" ` +
|
|
177
|
+
`--type String --overwrite` +
|
|
178
|
+
(region ? ` --region ${region}` : ''),
|
|
179
|
+
});
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const list = parseAllowList(paramValue);
|
|
183
|
+
const { found, matchedEntry } = checkMembership(list, check.needle, check.entryKeyExtractor);
|
|
184
|
+
if (!found) {
|
|
185
|
+
// Parameter exists but role not present.
|
|
186
|
+
if (check.id === '4c') {
|
|
187
|
+
// Return bare role slugs (strip :cadence) for row3b's includes() check.
|
|
188
|
+
triggersAllowlistValue = list.map((e) => check.entryKeyExtractor(e));
|
|
189
|
+
// r3.3: SKIP if dispatch-driven (no cadence playbook); FAIL only when the role
|
|
190
|
+
// has a cadence playbook that will fire with no entry in the triggers allowlist.
|
|
191
|
+
if (!hasCadencePlaybookFile(opts.playbookFiles)) {
|
|
192
|
+
results.push({
|
|
193
|
+
id: check.id,
|
|
194
|
+
name: check.name,
|
|
195
|
+
status: 'skip',
|
|
196
|
+
detail: `${check.needleLabel} not in ${check.paramPath} — ` +
|
|
197
|
+
`role is dispatch-driven; cadence trigger not required by design.`,
|
|
198
|
+
fix_command: null,
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const valueToAdd = check.newEntryValue ?? check.needle;
|
|
204
|
+
const newValue = [...list, valueToAdd].join(',');
|
|
205
|
+
results.push({
|
|
206
|
+
id: check.id,
|
|
207
|
+
name: check.name,
|
|
208
|
+
status: 'fail',
|
|
209
|
+
detail: `${check.needleLabel} not in ${check.paramPath} (current: [${list.join(', ')}]).`,
|
|
210
|
+
fix_command: `aws ssm put-parameter --name "${check.paramPath}" --value "${newValue}" ` +
|
|
211
|
+
`--type String --overwrite` +
|
|
212
|
+
(region ? ` --region ${region}` : ''),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
if (check.id === '4c') {
|
|
217
|
+
// Return bare role slugs (strip :cadence) for row3b's includes() check.
|
|
218
|
+
triggersAllowlistValue = list.map((e) => check.entryKeyExtractor(e));
|
|
219
|
+
}
|
|
220
|
+
results.push({
|
|
221
|
+
id: check.id,
|
|
222
|
+
name: check.name,
|
|
223
|
+
status: 'pass',
|
|
224
|
+
// For 4c: surface the full "role:cadence" matched entry — cadence is useful
|
|
225
|
+
// operator information (shows which schedule governs this role's auto-wake).
|
|
226
|
+
detail: matchedEntry !== undefined && matchedEntry !== check.needle
|
|
227
|
+
? `${check.needleLabel} found in ${check.paramPath} as '${matchedEntry}'`
|
|
228
|
+
: `${check.needleLabel} found in ${check.paramPath}`,
|
|
229
|
+
fix_command: null,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return { rows: results, triggersAllowlist: triggersAllowlistValue };
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=row4_ssm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row4_ssm.js","sourceRoot":"","sources":["../../src/preflight/row4_ssm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Row 5 — Worker Lambda wake config check.
|
|
3
|
+
*
|
|
4
|
+
* For each Lambda in PREFLIGHT_WORKER_LAMBDAS, verifies that the function's
|
|
5
|
+
* environment variables include the required wake/SSM config keys
|
|
6
|
+
* (WAKE_ALLOWLIST_PARAM, etc.) — the BUG153 gap.
|
|
7
|
+
*
|
|
8
|
+
* Uses `aws lambda get-function-configuration --function-name <name>`
|
|
9
|
+
* (shells out via execSync, consistent with existing CLI pattern).
|
|
10
|
+
*
|
|
11
|
+
* D2: enumeration is hard-coded with visible staleness (printed on every run).
|
|
12
|
+
*
|
|
13
|
+
* Fix line for row 5 FAILs: honest docs-link (no fake executable command —
|
|
14
|
+
* Ollie A3, rul_f53_fix_command_honesty).
|
|
15
|
+
*/
|
|
16
|
+
import type { PreflightRow } from './schema.js';
|
|
17
|
+
export declare function checkRow5WorkerLambdaWakeConfig(opts: {
|
|
18
|
+
env: string;
|
|
19
|
+
region?: string;
|
|
20
|
+
}): Promise<PreflightRow>;
|