@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,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 D3 — Remote workspace repo file reads via the operator's `gh` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Row 3 checks (persona, playbook, roles.json entry) are verified against
|
|
5
|
+
* `origin/main` of the workspace repo — not the local working copy — so
|
|
6
|
+
* an uncommitted state doesn't produce false PASSes or FAILes.
|
|
7
|
+
*/
|
|
8
|
+
export interface RepoRef {
|
|
9
|
+
owner: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
ref: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse a git remote URL into { owner, repo }.
|
|
15
|
+
* Handles HTTPS (https://github.com/owner/repo.git) and SSH (git@github.com:owner/repo.git).
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseGitHubUrl(url: string): {
|
|
18
|
+
owner: string;
|
|
19
|
+
repo: string;
|
|
20
|
+
} | null;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the GitHub owner/repo for a workspace by reading its git remote origin URL.
|
|
23
|
+
* Returns null if the workspace is not a GitHub repo or git is not available.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getWorkspaceGitHubRepo(workspaceRoot: string): RepoRef | null;
|
|
26
|
+
/**
|
|
27
|
+
* Check if `gh` CLI is installed and authenticated.
|
|
28
|
+
* Throws ToolingError if not.
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkGhAvailable(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a file exists in the GitHub repo at origin/main.
|
|
33
|
+
* Calls `gh api` to query the Contents API.
|
|
34
|
+
* Returns true if file exists, false if 404.
|
|
35
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkFileExistsOnGitHub(repoRef: RepoRef, filePath: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get file size in bytes from the GitHub repo at origin/main.
|
|
40
|
+
* Returns null if the file is not found (404).
|
|
41
|
+
* Returns 0 if the file exists but is empty.
|
|
42
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getFileSizeOnGitHub(repoRef: RepoRef, filePath: string): number | null;
|
|
45
|
+
/**
|
|
46
|
+
* List filenames in a directory on GitHub at origin/main.
|
|
47
|
+
* Returns null if the path is not found (404) or if the path is a file (not a dir).
|
|
48
|
+
* Returns an array of filenames if the path is a directory.
|
|
49
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
50
|
+
*/
|
|
51
|
+
export declare function listDirectoryFilenamesOnGitHub(repoRef: RepoRef, dirPath: string): string[] | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get the full decoded text content of a file from the GitHub repo at origin/main.
|
|
54
|
+
* Returns null if the file is not found (404).
|
|
55
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
56
|
+
*
|
|
57
|
+
* The GitHub Contents API returns base64-encoded content (≤1 MB). We decode it
|
|
58
|
+
* in Node.js after receiving the raw base64 string from gh. This is a content
|
|
59
|
+
* fetch (not just metadata), which is appropriate for the row-3a body check —
|
|
60
|
+
* persona files are small markdown docs and the extra payload is negligible.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getFileContentOnGitHub(repoRef: RepoRef, filePath: string): string | null;
|
|
63
|
+
export declare class ToolingError extends Error {
|
|
64
|
+
constructor(message: string);
|
|
65
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 D3 — Remote workspace repo file reads via the operator's `gh` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Row 3 checks (persona, playbook, roles.json entry) are verified against
|
|
5
|
+
* `origin/main` of the workspace repo — not the local working copy — so
|
|
6
|
+
* an uncommitted state doesn't produce false PASSes or FAILes.
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
/**
|
|
10
|
+
* Parse a git remote URL into { owner, repo }.
|
|
11
|
+
* Handles HTTPS (https://github.com/owner/repo.git) and SSH (git@github.com:owner/repo.git).
|
|
12
|
+
*/
|
|
13
|
+
export function parseGitHubUrl(url) {
|
|
14
|
+
// HTTPS
|
|
15
|
+
const httpsMatch = url.match(/github\.com\/([^/]+)\/([^/.]+?)(?:\.git)?\s*$/);
|
|
16
|
+
if (httpsMatch)
|
|
17
|
+
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
18
|
+
// SSH
|
|
19
|
+
const sshMatch = url.match(/github\.com:([^/]+)\/([^/.]+?)(?:\.git)?\s*$/);
|
|
20
|
+
if (sshMatch)
|
|
21
|
+
return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the GitHub owner/repo for a workspace by reading its git remote origin URL.
|
|
26
|
+
* Returns null if the workspace is not a GitHub repo or git is not available.
|
|
27
|
+
*/
|
|
28
|
+
export function getWorkspaceGitHubRepo(workspaceRoot) {
|
|
29
|
+
try {
|
|
30
|
+
const url = execSync('git config --get remote.origin.url', {
|
|
31
|
+
cwd: workspaceRoot,
|
|
32
|
+
encoding: 'utf-8',
|
|
33
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
34
|
+
}).trim();
|
|
35
|
+
const parsed = parseGitHubUrl(url);
|
|
36
|
+
if (!parsed)
|
|
37
|
+
return null;
|
|
38
|
+
return { ...parsed, ref: 'main' };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if `gh` CLI is installed and authenticated.
|
|
46
|
+
* Throws ToolingError if not.
|
|
47
|
+
*/
|
|
48
|
+
export function checkGhAvailable() {
|
|
49
|
+
try {
|
|
50
|
+
execSync('gh auth status', {
|
|
51
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
52
|
+
encoding: 'utf-8',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const msg = String(err.stderr ?? err.stdout ?? err.message ?? '');
|
|
57
|
+
if (msg.includes('not logged into any') || msg.includes('not logged in') || msg.includes('You are not logged')) {
|
|
58
|
+
throw new ToolingError('gh CLI is not authenticated. Run: gh auth login');
|
|
59
|
+
}
|
|
60
|
+
throw new ToolingError('gh CLI is not installed or not in PATH. Install with: brew install gh && gh auth login');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a file exists in the GitHub repo at origin/main.
|
|
65
|
+
* Calls `gh api` to query the Contents API.
|
|
66
|
+
* Returns true if file exists, false if 404.
|
|
67
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
68
|
+
*/
|
|
69
|
+
export function checkFileExistsOnGitHub(repoRef, filePath) {
|
|
70
|
+
// Encode the path for the URL, but keep slashes
|
|
71
|
+
const encodedPath = filePath.split('/').map(encodeURIComponent).join('/');
|
|
72
|
+
const url = `repos/${repoRef.owner}/${repoRef.repo}/contents/${encodedPath}?ref=${repoRef.ref}`;
|
|
73
|
+
try {
|
|
74
|
+
execSync(`gh api "${url}" --jq '.type'`, {
|
|
75
|
+
encoding: 'utf-8',
|
|
76
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
77
|
+
});
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const stderr = String(err.stderr ?? '');
|
|
82
|
+
const stdout = String(err.stdout ?? '');
|
|
83
|
+
if (stderr.includes('Not Found') ||
|
|
84
|
+
stderr.includes('404') ||
|
|
85
|
+
stdout.includes('Not Found') ||
|
|
86
|
+
stdout.includes('"Not Found"')) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
// Other error — gh broken or auth issue
|
|
90
|
+
const detail = (stderr || stdout || String(err.message ?? '')).trim();
|
|
91
|
+
throw new ToolingError(`gh api call failed for ${filePath}: ${detail}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get file size in bytes from the GitHub repo at origin/main.
|
|
96
|
+
* Returns null if the file is not found (404).
|
|
97
|
+
* Returns 0 if the file exists but is empty.
|
|
98
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
99
|
+
*/
|
|
100
|
+
export function getFileSizeOnGitHub(repoRef, filePath) {
|
|
101
|
+
const encodedPath = filePath.split('/').map(encodeURIComponent).join('/');
|
|
102
|
+
const url = `repos/${repoRef.owner}/${repoRef.repo}/contents/${encodedPath}?ref=${repoRef.ref}`;
|
|
103
|
+
try {
|
|
104
|
+
const result = execSync(`gh api "${url}" --jq '.size'`, {
|
|
105
|
+
encoding: 'utf-8',
|
|
106
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
107
|
+
}).trim();
|
|
108
|
+
const size = parseInt(result, 10);
|
|
109
|
+
return isNaN(size) ? 0 : size;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
const stderr = String(err.stderr ?? '');
|
|
113
|
+
const stdout = String(err.stdout ?? '');
|
|
114
|
+
if (stderr.includes('Not Found') ||
|
|
115
|
+
stderr.includes('404') ||
|
|
116
|
+
stdout.includes('Not Found') ||
|
|
117
|
+
stdout.includes('"Not Found"')) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const detail = (stderr || stdout || String(err.message ?? '')).trim();
|
|
121
|
+
throw new ToolingError(`gh api call failed for ${filePath}: ${detail}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* List filenames in a directory on GitHub at origin/main.
|
|
126
|
+
* Returns null if the path is not found (404) or if the path is a file (not a dir).
|
|
127
|
+
* Returns an array of filenames if the path is a directory.
|
|
128
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
129
|
+
*/
|
|
130
|
+
export function listDirectoryFilenamesOnGitHub(repoRef, dirPath) {
|
|
131
|
+
const encodedPath = dirPath.split('/').map(encodeURIComponent).join('/');
|
|
132
|
+
const url = `repos/${repoRef.owner}/${repoRef.repo}/contents/${encodedPath}?ref=${repoRef.ref}`;
|
|
133
|
+
try {
|
|
134
|
+
// The API returns an array for directories, an object for files.
|
|
135
|
+
// Use conditional jq to safely handle both: return null if not an array.
|
|
136
|
+
const result = execSync(`gh api "${url}" --jq 'if type == "array" then [.[].name] else null end'`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
137
|
+
if (result === 'null')
|
|
138
|
+
return null;
|
|
139
|
+
return JSON.parse(result);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
const stderr = String(err.stderr ?? '');
|
|
143
|
+
const stdout = String(err.stdout ?? '');
|
|
144
|
+
if (stderr.includes('Not Found') ||
|
|
145
|
+
stderr.includes('404') ||
|
|
146
|
+
stdout.includes('Not Found') ||
|
|
147
|
+
stdout.includes('"Not Found"')) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const detail = (stderr || stdout || String(err.message ?? '')).trim();
|
|
151
|
+
throw new ToolingError(`gh api directory listing failed for ${dirPath}: ${detail}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get the full decoded text content of a file from the GitHub repo at origin/main.
|
|
156
|
+
* Returns null if the file is not found (404).
|
|
157
|
+
* Throws ToolingError if gh fails for any other reason.
|
|
158
|
+
*
|
|
159
|
+
* The GitHub Contents API returns base64-encoded content (≤1 MB). We decode it
|
|
160
|
+
* in Node.js after receiving the raw base64 string from gh. This is a content
|
|
161
|
+
* fetch (not just metadata), which is appropriate for the row-3a body check —
|
|
162
|
+
* persona files are small markdown docs and the extra payload is negligible.
|
|
163
|
+
*/
|
|
164
|
+
export function getFileContentOnGitHub(repoRef, filePath) {
|
|
165
|
+
const encodedPath = filePath.split('/').map(encodeURIComponent).join('/');
|
|
166
|
+
const url = `repos/${repoRef.owner}/${repoRef.repo}/contents/${encodedPath}?ref=${repoRef.ref}`;
|
|
167
|
+
try {
|
|
168
|
+
// GitHub encodes file content as base64 with embedded newlines every 60 chars.
|
|
169
|
+
const rawB64 = execSync(`gh api "${url}" --jq '.content'`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
170
|
+
if (!rawB64 || rawB64 === 'null')
|
|
171
|
+
return null;
|
|
172
|
+
// Strip embedded newlines before decoding
|
|
173
|
+
return Buffer.from(rawB64.replace(/\n/g, ''), 'base64').toString('utf-8');
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const stderr = String(err.stderr ?? '');
|
|
177
|
+
const stdout = String(err.stdout ?? '');
|
|
178
|
+
if (stderr.includes('Not Found') ||
|
|
179
|
+
stderr.includes('404') ||
|
|
180
|
+
stdout.includes('Not Found') ||
|
|
181
|
+
stdout.includes('"Not Found"')) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const detail = (stderr || stdout || String(err.message ?? '')).trim();
|
|
185
|
+
throw new ToolingError(`gh api call failed for ${filePath}: ${detail}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export class ToolingError extends Error {
|
|
189
|
+
constructor(message) {
|
|
190
|
+
super(message);
|
|
191
|
+
this.name = 'ToolingError';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=repoRead.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repoRead.js","sourceRoot":"","sources":["../../src/preflight/repoRead.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 r3 D9 — Roster loader.
|
|
3
|
+
*
|
|
4
|
+
* Classifies every project×role pair as one of:
|
|
5
|
+
* - `declared_here`: workspace's roles.json (root or per-project) declares the role
|
|
6
|
+
* with a filler and a project field.
|
|
7
|
+
* - `declared_cross_workspace`: root roles.json `cross_workspace_roles` block declares
|
|
8
|
+
* the role by pointer (workspace_path + registry_path); no filler, no project.
|
|
9
|
+
* - (absence = `not_declared`): neither of the above.
|
|
10
|
+
*
|
|
11
|
+
* `declared_here` and `declared_cross_workspace` are two distinct positive states.
|
|
12
|
+
* `not_declared` is absence — not a positive entry.
|
|
13
|
+
*/
|
|
14
|
+
import type { RolesRegistry, RoleEntry } from '../dispatch.js';
|
|
15
|
+
export interface DeclaredHereEntry {
|
|
16
|
+
classification: 'declared_here';
|
|
17
|
+
project_slug: string;
|
|
18
|
+
role_slug: string;
|
|
19
|
+
filler: RoleEntry['filler'];
|
|
20
|
+
source_file: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DeclaredCrossWorkspaceEntry {
|
|
23
|
+
classification: 'declared_cross_workspace';
|
|
24
|
+
role_slug: string;
|
|
25
|
+
workspace_path: string;
|
|
26
|
+
registry_path: string;
|
|
27
|
+
source_file: string;
|
|
28
|
+
}
|
|
29
|
+
export interface Roster {
|
|
30
|
+
declared_here: DeclaredHereEntry[];
|
|
31
|
+
declared_cross_workspace: DeclaredCrossWorkspaceEntry[];
|
|
32
|
+
source_files: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the roster from a root registry (and any per-project registries under
|
|
36
|
+
* `<workspaceRoot>/projects/`).
|
|
37
|
+
*
|
|
38
|
+
* @param workspaceRoot - the workspace repo root (where .fazemos/roles.json lives)
|
|
39
|
+
* @param rootRegistry - the already-loaded root registry
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadRoster(workspaceRoot: string, rootRegistry: RolesRegistry): Roster;
|
|
42
|
+
/**
|
|
43
|
+
* Return the roster classification for a (project_slug, role_slug) pair.
|
|
44
|
+
* `declared_here` wins over `declared_cross_workspace`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function classifyRoleForProject(roster: Roster, project_slug: string, role_slug: string): 'declared_here' | 'declared_cross_workspace' | 'not_declared';
|
|
47
|
+
/**
|
|
48
|
+
* Return all declared-here entries for a given role_slug, plus whether it is
|
|
49
|
+
* cross-workspace-declared.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getProjectsForRole(roster: Roster, role_slug: string): {
|
|
52
|
+
declared_here: DeclaredHereEntry[];
|
|
53
|
+
declared_cross_workspace: DeclaredCrossWorkspaceEntry | null;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Return all declared-here roles for a given project_slug, plus the cross-workspace
|
|
57
|
+
* entries (which are project-agnostic — they apply to the whole workspace).
|
|
58
|
+
*/
|
|
59
|
+
export declare function getRolesForProject(roster: Roster, project_slug: string): {
|
|
60
|
+
declared_here: DeclaredHereEntry[];
|
|
61
|
+
declared_cross_workspace: DeclaredCrossWorkspaceEntry[];
|
|
62
|
+
};
|
|
63
|
+
export declare class RosterLoadError extends Error {
|
|
64
|
+
constructor(message: string);
|
|
65
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 r3 D9 — Roster loader.
|
|
3
|
+
*
|
|
4
|
+
* Classifies every project×role pair as one of:
|
|
5
|
+
* - `declared_here`: workspace's roles.json (root or per-project) declares the role
|
|
6
|
+
* with a filler and a project field.
|
|
7
|
+
* - `declared_cross_workspace`: root roles.json `cross_workspace_roles` block declares
|
|
8
|
+
* the role by pointer (workspace_path + registry_path); no filler, no project.
|
|
9
|
+
* - (absence = `not_declared`): neither of the above.
|
|
10
|
+
*
|
|
11
|
+
* `declared_here` and `declared_cross_workspace` are two distinct positive states.
|
|
12
|
+
* `not_declared` is absence — not a positive entry.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
// ── Loader ─────────────────────────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Build the roster from a root registry (and any per-project registries under
|
|
19
|
+
* `<workspaceRoot>/projects/`).
|
|
20
|
+
*
|
|
21
|
+
* @param workspaceRoot - the workspace repo root (where .fazemos/roles.json lives)
|
|
22
|
+
* @param rootRegistry - the already-loaded root registry
|
|
23
|
+
*/
|
|
24
|
+
export function loadRoster(workspaceRoot, rootRegistry) {
|
|
25
|
+
const declared_here = [];
|
|
26
|
+
const declared_cross_workspace = [];
|
|
27
|
+
const source_files = [];
|
|
28
|
+
function processRegistry(registry) {
|
|
29
|
+
if (!source_files.includes(registry._registryPath)) {
|
|
30
|
+
source_files.push(registry._registryPath);
|
|
31
|
+
}
|
|
32
|
+
const project_slug = registry.project ?? '';
|
|
33
|
+
// Locally-declared roles
|
|
34
|
+
for (const [role_slug, role] of Object.entries(registry.roles)) {
|
|
35
|
+
if (role.filler && project_slug) {
|
|
36
|
+
const exists = declared_here.some((e) => e.project_slug === project_slug && e.role_slug === role_slug);
|
|
37
|
+
if (!exists) {
|
|
38
|
+
declared_here.push({
|
|
39
|
+
classification: 'declared_here',
|
|
40
|
+
project_slug,
|
|
41
|
+
role_slug,
|
|
42
|
+
filler: role.filler,
|
|
43
|
+
source_file: registry._registryPath,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Cross-workspace pointers (only processed on root registry, where they live)
|
|
49
|
+
if (registry.cross_workspace_roles) {
|
|
50
|
+
for (const [role_slug, xref] of Object.entries(registry.cross_workspace_roles)) {
|
|
51
|
+
// Validate shape
|
|
52
|
+
if (!xref.workspace_path || !xref.registry_path) {
|
|
53
|
+
throw new RosterLoadError(`Malformed cross_workspace_roles entry for '${role_slug}' in ${registry._registryPath}: ` +
|
|
54
|
+
`must have workspace_path and registry_path.`);
|
|
55
|
+
}
|
|
56
|
+
const exists = declared_cross_workspace.some((e) => e.role_slug === role_slug);
|
|
57
|
+
if (!exists) {
|
|
58
|
+
declared_cross_workspace.push({
|
|
59
|
+
classification: 'declared_cross_workspace',
|
|
60
|
+
role_slug,
|
|
61
|
+
workspace_path: xref.workspace_path,
|
|
62
|
+
registry_path: xref.registry_path,
|
|
63
|
+
source_file: registry._registryPath,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Process root registry
|
|
70
|
+
processRegistry(rootRegistry);
|
|
71
|
+
// Process per-project registries (supports future layouts)
|
|
72
|
+
const projectsDir = join(workspaceRoot, 'projects');
|
|
73
|
+
if (existsSync(projectsDir)) {
|
|
74
|
+
try {
|
|
75
|
+
const entries = readdirSync(projectsDir);
|
|
76
|
+
for (const name of entries) {
|
|
77
|
+
const full = join(projectsDir, name);
|
|
78
|
+
try {
|
|
79
|
+
if (!statSync(full).isDirectory())
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const candidate = join(full, '.fazemos', 'roles.json');
|
|
86
|
+
if (existsSync(candidate)) {
|
|
87
|
+
try {
|
|
88
|
+
const raw = JSON.parse(readFileSync(candidate, 'utf-8'));
|
|
89
|
+
const registry = {
|
|
90
|
+
...raw,
|
|
91
|
+
_workspaceRoot: workspaceRoot,
|
|
92
|
+
_registryPath: candidate,
|
|
93
|
+
};
|
|
94
|
+
processRegistry(registry);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err instanceof RosterLoadError)
|
|
98
|
+
throw err;
|
|
99
|
+
// Malformed JSON or other issues — skip silently
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (err instanceof RosterLoadError)
|
|
106
|
+
throw err;
|
|
107
|
+
// Can't enumerate projects/ — continue
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { declared_here, declared_cross_workspace, source_files };
|
|
111
|
+
}
|
|
112
|
+
// ── Classification helpers ─────────────────────────────────────────────────────
|
|
113
|
+
/**
|
|
114
|
+
* Return the roster classification for a (project_slug, role_slug) pair.
|
|
115
|
+
* `declared_here` wins over `declared_cross_workspace`.
|
|
116
|
+
*/
|
|
117
|
+
export function classifyRoleForProject(roster, project_slug, role_slug) {
|
|
118
|
+
if (roster.declared_here.some((e) => e.project_slug === project_slug && e.role_slug === role_slug)) {
|
|
119
|
+
return 'declared_here';
|
|
120
|
+
}
|
|
121
|
+
if (roster.declared_cross_workspace.some((e) => e.role_slug === role_slug)) {
|
|
122
|
+
return 'declared_cross_workspace';
|
|
123
|
+
}
|
|
124
|
+
return 'not_declared';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Return all declared-here entries for a given role_slug, plus whether it is
|
|
128
|
+
* cross-workspace-declared.
|
|
129
|
+
*/
|
|
130
|
+
export function getProjectsForRole(roster, role_slug) {
|
|
131
|
+
return {
|
|
132
|
+
declared_here: roster.declared_here.filter((e) => e.role_slug === role_slug),
|
|
133
|
+
declared_cross_workspace: roster.declared_cross_workspace.find((e) => e.role_slug === role_slug) ?? null,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Return all declared-here roles for a given project_slug, plus the cross-workspace
|
|
138
|
+
* entries (which are project-agnostic — they apply to the whole workspace).
|
|
139
|
+
*/
|
|
140
|
+
export function getRolesForProject(roster, project_slug) {
|
|
141
|
+
return {
|
|
142
|
+
declared_here: roster.declared_here.filter((e) => e.project_slug === project_slug),
|
|
143
|
+
declared_cross_workspace: roster.declared_cross_workspace,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// ── Errors ────────────────────────────────────────────────────────────────────
|
|
147
|
+
export class RosterLoadError extends Error {
|
|
148
|
+
constructor(message) {
|
|
149
|
+
super(message);
|
|
150
|
+
this.name = 'RosterLoadError';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=roster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roster.js","sourceRoot":"","sources":["../../src/preflight/roster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA2B5B,kFAAkF;AAElF;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,aAAqB,EACrB,YAA2B;IAE3B,MAAM,aAAa,GAAwB,EAAE,CAAC;IAC9C,MAAM,wBAAwB,GAAkC,EAAE,CAAC;IACnE,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,SAAS,eAAe,CAAC,QAAuB;QAC9C,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACnD,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC;QAE5C,yBAAyB;QACzB,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/D,IAAI,IAAI,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CACpE,CAAC;gBACF,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,aAAa,CAAC,IAAI,CAAC;wBACjB,cAAc,EAAE,eAAe;wBAC/B,YAAY;wBACZ,SAAS;wBACT,MAAM,EAAE,IAAI,CAAC,MAAM;wBACnB,WAAW,EAAE,QAAQ,CAAC,aAAa;qBACpC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,8EAA8E;QAC9E,IAAI,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YACnC,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;gBAC/E,iBAAiB;gBACjB,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBAChD,MAAM,IAAI,eAAe,CACvB,8CAA8C,SAAS,QAAQ,QAAQ,CAAC,aAAa,IAAI;wBACzF,6CAA6C,CAC9C,CAAC;gBACJ,CAAC;gBACD,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;gBAC/E,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,wBAAwB,CAAC,IAAI,CAAC;wBAC5B,cAAc,EAAE,0BAA0B;wBAC1C,SAAS;wBACT,cAAc,EAAE,IAAI,CAAC,cAAc;wBACnC,aAAa,EAAE,IAAI,CAAC,aAAa;wBACjC,WAAW,EAAE,QAAQ,CAAC,aAAa;qBACpC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,wBAAwB;IACxB,eAAe,CAAC,YAAY,CAAC,CAAC;IAE9B,2DAA2D;IAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IACpD,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;YACzC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;gBAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBACrC,IAAI,CAAC;oBACH,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;wBAAE,SAAS;gBAC9C,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;gBACvD,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC1B,IAAI,CAAC;wBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAkB,CAAC;wBAC1E,MAAM,QAAQ,GAAkB;4BAC9B,GAAG,GAAG;4BACN,cAAc,EAAE,aAAa;4BAC7B,aAAa,EAAE,SAAS;yBACzB,CAAC;wBACF,eAAe,CAAC,QAAQ,CAAC,CAAC;oBAC5B,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,IAAI,GAAG,YAAY,eAAe;4BAAE,MAAM,GAAG,CAAC;wBAC9C,iDAAiD;oBACnD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,eAAe;gBAAE,MAAM,GAAG,CAAC;YAC9C,uCAAuC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,YAAY,EAAE,CAAC;AACnE,CAAC;AAED,kFAAkF;AAElF;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,YAAoB,EACpB,SAAiB;IAEjB,IAAI,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,EAAE,CAAC;QACnG,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,EAAE,CAAC;QAC3E,OAAO,0BAA0B,CAAC;IACpC,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,SAAiB;IAKjB,OAAO;QACL,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC;QAC5E,wBAAwB,EACtB,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,IAAI,IAAI;KACjF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,YAAoB;IAKpB,OAAO;QACL,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,CAAC;QAClF,wBAAwB,EAAE,MAAM,CAAC,wBAAwB;KAC1D,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Row 1 — Agent member exists in org with the target role_slug.
|
|
3
|
+
*
|
|
4
|
+
* Data source: GET /api/organizations/:orgId/members?type=agent
|
|
5
|
+
* Checks: (a) an agent member named agentName exists, (b) role_slug ∈ agent_config.roles.
|
|
6
|
+
*/
|
|
7
|
+
import type { PreflightRow } from './schema.js';
|
|
8
|
+
export declare function checkRow1Member(orgId: string, agentName: string, roleSlug: string): Promise<PreflightRow>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Row 1 — Agent member exists in org with the target role_slug.
|
|
3
|
+
*
|
|
4
|
+
* Data source: GET /api/organizations/:orgId/members?type=agent
|
|
5
|
+
* Checks: (a) an agent member named agentName exists, (b) role_slug ∈ agent_config.roles.
|
|
6
|
+
*/
|
|
7
|
+
import { api } from '../api.js';
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a display name for fuzzy matching (case-insensitive, collapse separators).
|
|
10
|
+
*/
|
|
11
|
+
function normName(s) {
|
|
12
|
+
return s.toLowerCase().replace(/[-_\s]+/g, ' ').trim();
|
|
13
|
+
}
|
|
14
|
+
export async function checkRow1Member(orgId, agentName, roleSlug) {
|
|
15
|
+
const base = { id: '1', name: 'agent member + credential' };
|
|
16
|
+
try {
|
|
17
|
+
const data = (await api('GET', `/api/organizations/${orgId}/members?type=agent`, undefined, { noProjectHeader: true }));
|
|
18
|
+
const members = data.members ?? [];
|
|
19
|
+
const agent = members.find((m) => m.member_type === 'agent' && normName(m.display_name) === normName(agentName));
|
|
20
|
+
if (!agent) {
|
|
21
|
+
return {
|
|
22
|
+
...base,
|
|
23
|
+
status: 'fail',
|
|
24
|
+
detail: `No agent member named '${agentName}' found in org (${members.filter((m) => m.member_type === 'agent').length} total agents).`,
|
|
25
|
+
fix_command: `fazemos agents register --name "${agentName}" --model sonnet`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const roles = agent.agent_config?.roles ?? [];
|
|
29
|
+
if (!roles.includes(roleSlug)) {
|
|
30
|
+
return {
|
|
31
|
+
...base,
|
|
32
|
+
status: 'fail',
|
|
33
|
+
detail: `Agent '${agentName}' (id=${agent.id}) found but role '${roleSlug}' not in agent_config.roles: [${roles.join(', ') || 'empty'}].`,
|
|
34
|
+
fix_command: null,
|
|
35
|
+
fix_instruction: `Update agent '${agentName}' config to include role '${roleSlug}' in its roles array. Re-run: fazemos agents register --name "${agentName}" --roles ${roleSlug}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...base,
|
|
40
|
+
status: 'pass',
|
|
41
|
+
detail: `${agentName} (id=${agent.id}), roles=[${roles.join(', ')}]`,
|
|
42
|
+
fix_command: null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (err?.status === 403 || err?.status === 401) {
|
|
47
|
+
return {
|
|
48
|
+
...base,
|
|
49
|
+
status: 'tooling_error',
|
|
50
|
+
detail: 'Access denied — owner/admin role required to list agent members.',
|
|
51
|
+
fix_command: null,
|
|
52
|
+
fix_instruction: 'Contact your org admin to grant owner or admin role, then retry.',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
...base,
|
|
57
|
+
status: 'tooling_error',
|
|
58
|
+
detail: `API call failed: ${err?.message ?? String(err)}`,
|
|
59
|
+
fix_command: null,
|
|
60
|
+
fix_instruction: 'Check your Fazemos API credentials and active org, then retry.',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=row1_member.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row1_member.js","sourceRoot":"","sources":["../../src/preflight/row1_member.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAchC;;GAEG;AACH,SAAS,QAAQ,CAAC,CAAS;IACzB,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,SAAiB,EACjB,QAAgB;IAEhB,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC;IAE5D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CACrB,KAAK,EACL,sBAAsB,KAAK,qBAAqB,EAChD,SAAS,EACT,EAAE,eAAe,EAAE,IAAI,EAAE,CAC1B,CAA8B,CAAC;QAEhC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,CACrF,CAAC;QAEF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;gBACL,GAAG,IAAI;gBACP,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,0BAA0B,SAAS,mBAAmB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,OAAO,CAAC,CAAC,MAAM,iBAAiB;gBACtI,WAAW,EAAE,mCAAmC,SAAS,kBAAkB;aAC5E,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO;gBACL,GAAG,IAAI;gBACP,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,UAAU,SAAS,SAAS,KAAK,CAAC,EAAE,qBAAqB,QAAQ,iCAAiC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI;gBACzI,WAAW,EAAE,IAAI;gBACjB,eAAe,EAAE,iBAAiB,SAAS,6BAA6B,QAAQ,iEAAiE,SAAS,aAAa,QAAQ,EAAE;aAClL,CAAC;QACJ,CAAC;QAED,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,SAAS,QAAQ,KAAK,CAAC,EAAE,aAAa,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YACpE,WAAW,EAAE,IAAI;SAClB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;YAC/C,OAAO;gBACL,GAAG,IAAI;gBACP,MAAM,EAAE,eAAe;gBACvB,MAAM,EAAE,kEAAkE;gBAC1E,WAAW,EAAE,IAAI;gBACjB,eAAe,EAAE,kEAAkE;aACpF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,eAAe;YACvB,MAAM,EAAE,oBAAoB,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE;YACzD,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,gEAAgE;SAClF,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F53 Row 2 — role_registrations row exists, active, and bound to the correct
|
|
3
|
+
* (org_id, project_id). Also verifies resolver-key parity (r3 D8).
|
|
4
|
+
*
|
|
5
|
+
* Data source: GET /api/role-registrations/lookup?workspace=&role_slug=&org_id=<uuid>
|
|
6
|
+
* Outcome: PASS | FAIL(no_registration) | FAIL(bound_elsewhere) |
|
|
7
|
+
* FAIL(resolver_ambiguous) | FAIL(resolver_mismatch)
|
|
8
|
+
*/
|
|
9
|
+
import type { PreflightRow } from './schema.js';
|
|
10
|
+
export declare function checkRow2RoleRegistration(opts: {
|
|
11
|
+
workspace: string;
|
|
12
|
+
roleSlug: string;
|
|
13
|
+
orgId: string;
|
|
14
|
+
projectId: string | null;
|
|
15
|
+
orgSlug: string;
|
|
16
|
+
projectSlug: string;
|
|
17
|
+
env: string;
|
|
18
|
+
}): Promise<PreflightRow>;
|