forgeos 0.1.0-alpha.50 → 0.1.0-alpha.52
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/AGENTS.md +1 -1
- package/CHANGELOG.md +37 -0
- package/docs/changelog.md +27 -0
- package/package.json +1 -1
- package/src/forge/_generated/releaseManifest.json +1 -1
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/cli/deploy.ts +20 -16
- package/src/forge/cli/workos.ts +220 -3
- package/src/forge/version.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.52 input=9a95853ed8e82db41be0b0cc4fc809e0b1c0c12fd1ac024d386bc651a70d965b content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
|
|
2
2
|
# AGENTS.md
|
|
3
3
|
|
|
4
4
|
<!-- forge-generated:start -->
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# forgeos
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.52
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Make WorkOS FGA real-rollout failures produce a concrete operator artifact.
|
|
8
|
+
|
|
9
|
+
- Generated WorkOS FGA setup guides now include an explicit automation
|
|
10
|
+
boundary explaining that ForgeOS derives the graph, syncs resources, and
|
|
11
|
+
proves Authorization API checks, but does not invent unsupported WorkOS
|
|
12
|
+
resource-type creation commands.
|
|
13
|
+
- `forge workos fga sync --real --write --json` now remains useful when the
|
|
14
|
+
WorkOS Authorization API reports missing resource types: the JSON includes
|
|
15
|
+
`setupGuidePath` and the written guide lists the resource types, parents,
|
|
16
|
+
permissions, roles, example external IDs, membership env, and rerun
|
|
17
|
+
commands needed to finish the hosted setup.
|
|
18
|
+
|
|
19
|
+
## 0.1.0-alpha.51
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Make the WorkOS FGA production doctor explicit enough for real P2 rollout.
|
|
24
|
+
|
|
25
|
+
- `forge workos fga doctor --real --json` now reports structured readiness
|
|
26
|
+
for the full hosted path: FGA plan, hosted seed evidence, hosted resource
|
|
27
|
+
type/resource sync, `organizationMembershipId` env, real proof state, and
|
|
28
|
+
production readiness.
|
|
29
|
+
- FGA JSON output now includes `membershipEnv`, `seedState`, and `readiness`
|
|
30
|
+
metadata so agents can choose the next command without scraping human text.
|
|
31
|
+
- Hosted FGA setup data now states that WorkOS resource type creation is not
|
|
32
|
+
automated by the WorkOS API/CLI path, while resource sync and authorization
|
|
33
|
+
checks remain ForgeOS-driven after those resource types exist.
|
|
34
|
+
- WorkOS real checks now read `deploy/.env.production` as an explicit deploy
|
|
35
|
+
evidence source, and `forge deploy check --production` runs WorkOS/FGA
|
|
36
|
+
doctors with those values in scope.
|
|
37
|
+
- Human output now distinguishes local FGA planning success from real
|
|
38
|
+
production-gate readiness.
|
|
39
|
+
|
|
3
40
|
## 0.1.0-alpha.50
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
package/docs/changelog.md
CHANGED
|
@@ -6,6 +6,33 @@ The canonical source file in the repository is `CHANGELOG.md`.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.1.0-alpha.52
|
|
10
|
+
|
|
11
|
+
- Generated WorkOS FGA setup guides now include an explicit automation boundary
|
|
12
|
+
describing what ForgeOS can automate versus the hosted WorkOS resource-type
|
|
13
|
+
configuration that must exist before real resource sync.
|
|
14
|
+
- `forge workos fga sync --real --write --json` now provides a concrete
|
|
15
|
+
`setupGuidePath` even when real sync fails because hosted WorkOS resource
|
|
16
|
+
types are missing, so agents have a deterministic artifact for the remaining
|
|
17
|
+
setup and rerun commands.
|
|
18
|
+
|
|
19
|
+
## 0.1.0-alpha.51
|
|
20
|
+
|
|
21
|
+
- `forge workos fga doctor --real --json` now reports a structured readiness
|
|
22
|
+
object for the full hosted FGA path: app-derived plan, hosted seed state,
|
|
23
|
+
real resource sync, membership env for WorkOS access checks, proof state, and
|
|
24
|
+
production readiness.
|
|
25
|
+
- FGA command JSON now includes `membershipEnv`, `seedState`, and `readiness`
|
|
26
|
+
metadata so agents can continue the no-dashboard WorkOS flow deterministically.
|
|
27
|
+
- Hosted FGA setup output now explicitly states that WorkOS resource type
|
|
28
|
+
creation is hosted WorkOS configuration, while ForgeOS can still sync
|
|
29
|
+
resources and run Authorization API checks after those types exist.
|
|
30
|
+
- WorkOS real checks now read `deploy/.env.production` as deploy evidence, and
|
|
31
|
+
`forge deploy check --production` runs WorkOS/FGA doctors with those values
|
|
32
|
+
in scope.
|
|
33
|
+
- Human FGA output now separates local planning success from real production
|
|
34
|
+
gate readiness.
|
|
35
|
+
|
|
9
36
|
## 0.1.0-alpha.50
|
|
10
37
|
|
|
11
38
|
- WorkOS hosted setup now includes explicit
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.
|
|
1
|
+
{"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.52","releaseId":"forgeos@0.1.0-alpha.52+unknown","schemaVersion":"0.1.0"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.52 input=9a95853ed8e82db41be0b0cc4fc809e0b1c0c12fd1ac024d386bc651a70d965b content=d4d4c9fd5f2fe5a5d3fa127d43487901cf1e68875b7ac99abdfe9a9cc90265ee
|
|
2
2
|
export const releaseManifest = {
|
|
3
3
|
"defaultProvider": "local",
|
|
4
4
|
"diagnostics": [],
|
|
@@ -19,7 +19,7 @@ export const releaseManifest = {
|
|
|
19
19
|
"custom"
|
|
20
20
|
],
|
|
21
21
|
"packageName": "forgeos",
|
|
22
|
-
"packageVersion": "0.1.0-alpha.
|
|
23
|
-
"releaseId": "forgeos@0.1.0-alpha.
|
|
22
|
+
"packageVersion": "0.1.0-alpha.52",
|
|
23
|
+
"releaseId": "forgeos@0.1.0-alpha.52+unknown",
|
|
24
24
|
"schemaVersion": "0.1.0"
|
|
25
25
|
} as const;
|
package/src/forge/cli/deploy.ts
CHANGED
|
@@ -821,13 +821,15 @@ async function buildChecks(options: DeployCommandOptions): Promise<DeployCommand
|
|
|
821
821
|
},
|
|
822
822
|
});
|
|
823
823
|
if (hasWorkOSIntegration(options.workspaceRoot)) {
|
|
824
|
-
const workosDoctor =
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
824
|
+
const workosDoctor = await withDeployEnv(options.workspaceRoot, async () =>
|
|
825
|
+
runWorkOSCommand({
|
|
826
|
+
subcommand: "doctor",
|
|
827
|
+
workspaceRoot: options.workspaceRoot,
|
|
828
|
+
json: true,
|
|
829
|
+
yes: false,
|
|
830
|
+
dryRun: true,
|
|
831
|
+
})
|
|
832
|
+
);
|
|
831
833
|
checks.push({
|
|
832
834
|
name: "workos-doctor",
|
|
833
835
|
ok: workosDoctor.exitCode === 0,
|
|
@@ -853,15 +855,17 @@ async function buildChecks(options: DeployCommandOptions): Promise<DeployCommand
|
|
|
853
855
|
command: "forge workos prove --real --file workos-seed.yml --json",
|
|
854
856
|
details: seedState,
|
|
855
857
|
});
|
|
856
|
-
const workosFgaDoctor =
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
858
|
+
const workosFgaDoctor = await withDeployEnv(options.workspaceRoot, async () =>
|
|
859
|
+
runWorkOSCommand({
|
|
860
|
+
subcommand: "fga",
|
|
861
|
+
fgaAction: "doctor",
|
|
862
|
+
workspaceRoot: options.workspaceRoot,
|
|
863
|
+
json: true,
|
|
864
|
+
yes: false,
|
|
865
|
+
dryRun: true,
|
|
866
|
+
real: options.production,
|
|
867
|
+
})
|
|
868
|
+
);
|
|
865
869
|
checks.push({
|
|
866
870
|
name: "workos-fga-proof",
|
|
867
871
|
ok: !options.production || workosFgaDoctor.exitCode === 0,
|
package/src/forge/cli/workos.ts
CHANGED
|
@@ -324,6 +324,7 @@ function parseEnvText(text: string): Record<string, string> {
|
|
|
324
324
|
function readRealEnv(workspaceRoot: string): Record<string, string> {
|
|
325
325
|
return {
|
|
326
326
|
...parseEnvText(readRawText(workspaceRoot, ".env")),
|
|
327
|
+
...parseEnvText(readRawText(workspaceRoot, "deploy/.env.production")),
|
|
327
328
|
...parseEnvText(readRawText(workspaceRoot, ".env.local")),
|
|
328
329
|
...Object.fromEntries(
|
|
329
330
|
Object.entries(process.env)
|
|
@@ -677,12 +678,21 @@ export interface WorkOSFgaStateSummary {
|
|
|
677
678
|
diagnostics: string[];
|
|
678
679
|
}
|
|
679
680
|
|
|
681
|
+
export interface WorkOSFgaMembershipEnvSummary {
|
|
682
|
+
requiredEnv: string[];
|
|
683
|
+
presentEnv: string[];
|
|
684
|
+
missingEnv: string[];
|
|
685
|
+
jsonEnvPresent: boolean;
|
|
686
|
+
complete: boolean;
|
|
687
|
+
}
|
|
688
|
+
|
|
680
689
|
export interface WorkOSFgaHostedSetup {
|
|
681
690
|
requiredResourceTypes: string[];
|
|
682
691
|
rootResourceType: "organization";
|
|
683
692
|
missingResourceTypes: string[];
|
|
684
693
|
requiredMembershipEnv: string[];
|
|
685
694
|
managedBy: "hosted-workos";
|
|
695
|
+
resourceTypeAutomation: "not-supported-by-workos-api" | "not-needed";
|
|
686
696
|
cliSupport: "resources-and-checks";
|
|
687
697
|
sdkSupport: "resources-and-checks";
|
|
688
698
|
docs: string[];
|
|
@@ -710,6 +720,19 @@ export interface WorkOSFgaSetupGuide {
|
|
|
710
720
|
unsupportedAutomation: string[];
|
|
711
721
|
}
|
|
712
722
|
|
|
723
|
+
export interface WorkOSFgaReadiness {
|
|
724
|
+
real: boolean;
|
|
725
|
+
planReady: boolean;
|
|
726
|
+
seedReady: boolean;
|
|
727
|
+
resourceTypesConfigured: boolean;
|
|
728
|
+
membershipEnvReady: boolean;
|
|
729
|
+
synced: boolean;
|
|
730
|
+
proved: boolean;
|
|
731
|
+
productionReady: boolean;
|
|
732
|
+
nextCommand: string;
|
|
733
|
+
nextActions: string[];
|
|
734
|
+
}
|
|
735
|
+
|
|
713
736
|
function slugifyExternalIdPart(value: string): string {
|
|
714
737
|
return value
|
|
715
738
|
.trim()
|
|
@@ -951,6 +974,24 @@ function fgaMembershipEnvKey(organization: string): string {
|
|
|
951
974
|
return `WORKOS_FGA_MEMBERSHIP_${suffix}`;
|
|
952
975
|
}
|
|
953
976
|
|
|
977
|
+
function workOSFgaMembershipEnvSummary(
|
|
978
|
+
workspaceRoot: string,
|
|
979
|
+
organizations: string[],
|
|
980
|
+
): WorkOSFgaMembershipEnvSummary {
|
|
981
|
+
const env = readRealEnv(workspaceRoot);
|
|
982
|
+
const requiredEnv = organizations.map(fgaMembershipEnvKey);
|
|
983
|
+
const jsonEnvPresent = hasValue(env, "WORKOS_FGA_MEMBERSHIPS_JSON") || hasValue(env, "WORKOS_FGA_TEST_MEMBERSHIPS");
|
|
984
|
+
const presentEnv = requiredEnv.filter((name) => hasValue(env, name));
|
|
985
|
+
const missingEnv = jsonEnvPresent ? [] : requiredEnv.filter((name) => !presentEnv.includes(name));
|
|
986
|
+
return {
|
|
987
|
+
requiredEnv,
|
|
988
|
+
presentEnv,
|
|
989
|
+
missingEnv,
|
|
990
|
+
jsonEnvPresent,
|
|
991
|
+
complete: jsonEnvPresent || missingEnv.length === 0,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
954
995
|
function extractMissingWorkOSFgaResourceTypes(data: unknown, manifest?: WorkOSFgaManifest): string[] {
|
|
955
996
|
const missing = new Set<string>();
|
|
956
997
|
const add = (value: unknown) => {
|
|
@@ -999,6 +1040,7 @@ function workOSFgaHostedSetup(manifest: WorkOSFgaManifest, sdkData?: unknown): W
|
|
|
999
1040
|
missingResourceTypes,
|
|
1000
1041
|
requiredMembershipEnv,
|
|
1001
1042
|
managedBy: "hosted-workos",
|
|
1043
|
+
resourceTypeAutomation: requiredResourceTypes.length > 0 ? "not-supported-by-workos-api" : "not-needed",
|
|
1002
1044
|
cliSupport: "resources-and-checks",
|
|
1003
1045
|
sdkSupport: "resources-and-checks",
|
|
1004
1046
|
docs: [
|
|
@@ -1107,6 +1149,13 @@ function workOSFgaSetupGuide(manifest: WorkOSFgaManifest, hostedSetup: WorkOSFga
|
|
|
1107
1149
|
"ForgeOS derived this resource graph from the app contract, policies, and workos-seed.yml.",
|
|
1108
1150
|
"WorkOS resource type configuration is hosted WorkOS configuration. ForgeOS uses the WorkOS CLI/API or SDK to sync resources and prove authorization checks after those resource types exist.",
|
|
1109
1151
|
"",
|
|
1152
|
+
"## Automation Boundary",
|
|
1153
|
+
"",
|
|
1154
|
+
"- ForgeOS can derive the FGA graph, generate this setup guide, sync WorkOS FGA resources, and run Authorization API access checks.",
|
|
1155
|
+
"- ForgeOS does not invent WorkOS CLI/API calls for resource type creation. The current public WorkOS automation surface supports resources and checks after resource types exist.",
|
|
1156
|
+
"- Configure each non-organization resource type below in hosted WorkOS, then rerun the real sync/proof commands.",
|
|
1157
|
+
"- The `organization` node is treated as the WorkOS tenant root and is not created as an authorization resource.",
|
|
1158
|
+
"",
|
|
1110
1159
|
"## Resource Types",
|
|
1111
1160
|
"",
|
|
1112
1161
|
...resourceTypes.flatMap((resourceType) => [
|
|
@@ -1179,11 +1228,14 @@ function writeWorkOSFgaSetupGuide(
|
|
|
1179
1228
|
|
|
1180
1229
|
function fgaData(input: {
|
|
1181
1230
|
action: WorkOSFgaAction;
|
|
1231
|
+
workspaceRoot: string;
|
|
1182
1232
|
manifest: WorkOSFgaManifest;
|
|
1183
1233
|
state: WorkOSFgaStateSummary;
|
|
1234
|
+
seedState?: WorkOSSeedStateSummary;
|
|
1184
1235
|
real?: boolean;
|
|
1185
1236
|
cliAuth?: WorkOSCliAuthSummary;
|
|
1186
1237
|
workosSdk?: unknown;
|
|
1238
|
+
readiness?: WorkOSFgaReadiness;
|
|
1187
1239
|
stateFile?: string;
|
|
1188
1240
|
nextCommand?: string;
|
|
1189
1241
|
nextActions?: string[];
|
|
@@ -1191,12 +1243,16 @@ function fgaData(input: {
|
|
|
1191
1243
|
}): Record<string, unknown> {
|
|
1192
1244
|
const hostedSetup = workOSFgaHostedSetup(input.manifest, input.workosSdk);
|
|
1193
1245
|
const setupGuide = workOSFgaSetupGuide(input.manifest, hostedSetup);
|
|
1246
|
+
const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
|
|
1194
1247
|
return {
|
|
1195
1248
|
action: input.action,
|
|
1196
1249
|
real: input.real ?? false,
|
|
1197
1250
|
manifest: input.manifest,
|
|
1198
1251
|
state: input.state,
|
|
1252
|
+
...(input.seedState ? { seedState: input.seedState } : {}),
|
|
1253
|
+
...(input.readiness ? { readiness: input.readiness } : {}),
|
|
1199
1254
|
hostedSetup,
|
|
1255
|
+
membershipEnv,
|
|
1200
1256
|
resourceTypeSetup: setupGuide.resourceTypes,
|
|
1201
1257
|
setupGuide,
|
|
1202
1258
|
...(input.cliAuth ? { cliAuth: input.cliAuth } : {}),
|
|
@@ -2573,6 +2629,133 @@ function collectWorkOSFgaChecks(input: {
|
|
|
2573
2629
|
];
|
|
2574
2630
|
}
|
|
2575
2631
|
|
|
2632
|
+
function workOSFgaReadiness(input: {
|
|
2633
|
+
workspaceRoot: string;
|
|
2634
|
+
file: string;
|
|
2635
|
+
manifest: WorkOSFgaManifest;
|
|
2636
|
+
state: WorkOSFgaStateSummary;
|
|
2637
|
+
seedState: WorkOSSeedStateSummary;
|
|
2638
|
+
real: boolean;
|
|
2639
|
+
}): WorkOSFgaReadiness {
|
|
2640
|
+
const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
|
|
2641
|
+
const planReady = input.manifest.diagnostics.length === 0 &&
|
|
2642
|
+
input.manifest.resourceTypes.includes("organization") &&
|
|
2643
|
+
input.manifest.proofScenarios.some((scenario) => scenario.expected === "allow") &&
|
|
2644
|
+
input.manifest.proofScenarios.some((scenario) => scenario.expected === "deny");
|
|
2645
|
+
const seedReady = !input.real || Boolean(input.seedState.exists && input.seedState.valid && input.seedState.matchesSeedHash === true);
|
|
2646
|
+
const synced = Boolean(input.state.exists && input.state.valid && input.state.matchesManifestHash === true);
|
|
2647
|
+
const resourceTypesConfigured = !input.real || Boolean(synced && input.state.mode === "real" && input.state.sdkOk === true);
|
|
2648
|
+
const membershipEnvReady = !input.real || membershipEnv.complete;
|
|
2649
|
+
const proved = !input.real
|
|
2650
|
+
? Boolean(input.state.provedAt)
|
|
2651
|
+
: Boolean(synced && input.state.mode === "real" && input.state.sdkOk === true && input.state.provedAt);
|
|
2652
|
+
const productionReady = Boolean(planReady && seedReady && resourceTypesConfigured && membershipEnvReady && proved);
|
|
2653
|
+
let nextCommand = `forge workos fga plan --file ${input.file} --write --json`;
|
|
2654
|
+
if (planReady && input.real && !seedReady) {
|
|
2655
|
+
nextCommand = `forge workos prove --real --file ${input.file} --json`;
|
|
2656
|
+
} else if (planReady && input.real && !resourceTypesConfigured) {
|
|
2657
|
+
nextCommand = `forge workos fga sync --real --file ${input.file} --write --json`;
|
|
2658
|
+
} else if (planReady && input.real && !membershipEnvReady) {
|
|
2659
|
+
nextCommand = `forge workos fga prove --real --file ${input.file} --json`;
|
|
2660
|
+
} else if (planReady && input.real && !proved) {
|
|
2661
|
+
nextCommand = `forge workos fga prove --real --file ${input.file} --json`;
|
|
2662
|
+
} else if (planReady && !input.real && !synced) {
|
|
2663
|
+
nextCommand = `forge workos fga sync --file ${input.file} --json`;
|
|
2664
|
+
} else if (planReady && !input.real && !proved) {
|
|
2665
|
+
nextCommand = `forge workos fga prove --file ${input.file} --json`;
|
|
2666
|
+
} else if (productionReady || (planReady && !input.real)) {
|
|
2667
|
+
nextCommand = "forge deploy check --production --json";
|
|
2668
|
+
}
|
|
2669
|
+
const nextActions = [
|
|
2670
|
+
...(planReady ? [] : [`repair FGA manifest gaps, then run forge workos fga plan --file ${input.file} --write --json`]),
|
|
2671
|
+
...(input.real && !seedReady ? [`apply/prove hosted WorkOS seed: forge workos prove --real --file ${input.file} --json`] : []),
|
|
2672
|
+
...(input.real && !resourceTypesConfigured
|
|
2673
|
+
? [
|
|
2674
|
+
"configure hosted WorkOS FGA resource types listed in resourceTypeSetup",
|
|
2675
|
+
`sync real WorkOS FGA resources: forge workos fga sync --real --file ${input.file} --write --json`,
|
|
2676
|
+
]
|
|
2677
|
+
: []),
|
|
2678
|
+
...(input.real && !membershipEnvReady
|
|
2679
|
+
? [`set WORKOS_FGA_MEMBERSHIPS_JSON or ${membershipEnv.missingEnv.join(", ") || "WORKOS_FGA_MEMBERSHIP_<ORG>"} before real access checks`]
|
|
2680
|
+
: []),
|
|
2681
|
+
...(input.real && membershipEnvReady && resourceTypesConfigured && !proved
|
|
2682
|
+
? [`prove real WorkOS FGA access checks: forge workos fga prove --real --file ${input.file} --json`]
|
|
2683
|
+
: []),
|
|
2684
|
+
...(productionReady ? ["rerun forge deploy check --production --json"] : []),
|
|
2685
|
+
...(!input.real && planReady && !synced ? [`run forge workos fga sync --file ${input.file} --json`] : []),
|
|
2686
|
+
...(!input.real && planReady && synced && !proved ? [`run forge workos fga prove --file ${input.file} --json`] : []),
|
|
2687
|
+
];
|
|
2688
|
+
return {
|
|
2689
|
+
real: input.real,
|
|
2690
|
+
planReady,
|
|
2691
|
+
seedReady,
|
|
2692
|
+
resourceTypesConfigured,
|
|
2693
|
+
membershipEnvReady,
|
|
2694
|
+
synced,
|
|
2695
|
+
proved,
|
|
2696
|
+
productionReady,
|
|
2697
|
+
nextCommand,
|
|
2698
|
+
nextActions,
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
function collectWorkOSFgaDoctorChecks(input: {
|
|
2703
|
+
workspaceRoot: string;
|
|
2704
|
+
file: string;
|
|
2705
|
+
manifest: WorkOSFgaManifest;
|
|
2706
|
+
state: WorkOSFgaStateSummary;
|
|
2707
|
+
seedState: WorkOSSeedStateSummary;
|
|
2708
|
+
real: boolean;
|
|
2709
|
+
}): WorkOSCheck[] {
|
|
2710
|
+
const readiness = workOSFgaReadiness(input);
|
|
2711
|
+
const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
|
|
2712
|
+
return [
|
|
2713
|
+
...collectWorkOSFgaChecks({
|
|
2714
|
+
manifest: input.manifest,
|
|
2715
|
+
state: input.state,
|
|
2716
|
+
requireState: input.real,
|
|
2717
|
+
requireRealState: input.real,
|
|
2718
|
+
requireProof: input.real,
|
|
2719
|
+
}),
|
|
2720
|
+
{
|
|
2721
|
+
name: "fga-seed-state",
|
|
2722
|
+
ok: !input.real || readiness.seedReady,
|
|
2723
|
+
detail: !input.real
|
|
2724
|
+
? "hosted seed evidence is only required for --real doctor"
|
|
2725
|
+
: readiness.seedReady
|
|
2726
|
+
? `${WORKOS_SEED_STATE_FILE} matches ${input.file}`
|
|
2727
|
+
: `real FGA proof requires hosted seed evidence matching ${input.file}; run forge workos prove --real --file ${input.file} --json`,
|
|
2728
|
+
},
|
|
2729
|
+
{
|
|
2730
|
+
name: "fga-hosted-resource-types",
|
|
2731
|
+
ok: !input.real || readiness.resourceTypesConfigured,
|
|
2732
|
+
detail: !input.real
|
|
2733
|
+
? "hosted WorkOS resource type existence is only verified by --real sync/prove"
|
|
2734
|
+
: readiness.resourceTypesConfigured
|
|
2735
|
+
? `${WORKOS_FGA_STATE_FILE} records successful real Authorization API resource sync`
|
|
2736
|
+
: "real FGA requires hosted WorkOS resource types for every non-organization resource; run forge workos fga plan --write and configure any listed resource types before sync",
|
|
2737
|
+
},
|
|
2738
|
+
{
|
|
2739
|
+
name: "fga-membership-env",
|
|
2740
|
+
ok: !input.real || readiness.membershipEnvReady,
|
|
2741
|
+
detail: !input.real
|
|
2742
|
+
? "organizationMembershipId env is only required for real WorkOS access checks"
|
|
2743
|
+
: readiness.membershipEnvReady
|
|
2744
|
+
? `membership env is present${membershipEnv.jsonEnvPresent ? " through WORKOS_FGA_MEMBERSHIPS_JSON" : ` through ${membershipEnv.presentEnv.join(", ")}`}`
|
|
2745
|
+
: `missing organizationMembershipId env for real checks: WORKOS_FGA_MEMBERSHIPS_JSON or ${membershipEnv.missingEnv.join(", ")}`,
|
|
2746
|
+
},
|
|
2747
|
+
{
|
|
2748
|
+
name: "fga-production-readiness",
|
|
2749
|
+
ok: !input.real || readiness.productionReady,
|
|
2750
|
+
detail: !input.real
|
|
2751
|
+
? "run forge workos fga doctor --real --json for production FGA gates"
|
|
2752
|
+
: readiness.productionReady
|
|
2753
|
+
? "real WorkOS FGA seed, resource sync, membership env, and proof are current"
|
|
2754
|
+
: `real WorkOS FGA is not production-ready; next command: ${readiness.nextCommand}`,
|
|
2755
|
+
},
|
|
2756
|
+
];
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2576
2759
|
export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSCommandResult {
|
|
2577
2760
|
const action = options.fgaAction ?? "doctor";
|
|
2578
2761
|
const file = options.file ?? DEFAULT_SEED_FILE;
|
|
@@ -2608,8 +2791,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2608
2791
|
applied: false,
|
|
2609
2792
|
data: fgaData({
|
|
2610
2793
|
action,
|
|
2794
|
+
workspaceRoot: options.workspaceRoot,
|
|
2611
2795
|
manifest,
|
|
2612
2796
|
state,
|
|
2797
|
+
seedState,
|
|
2613
2798
|
real,
|
|
2614
2799
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
2615
2800
|
nextCommand: `forge workos fga sync --file ${file} --json`,
|
|
@@ -2642,8 +2827,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2642
2827
|
applied: false,
|
|
2643
2828
|
data: fgaData({
|
|
2644
2829
|
action,
|
|
2830
|
+
workspaceRoot: options.workspaceRoot,
|
|
2645
2831
|
manifest,
|
|
2646
2832
|
state,
|
|
2833
|
+
seedState,
|
|
2647
2834
|
real,
|
|
2648
2835
|
...(cliAuth ? { cliAuth } : {}),
|
|
2649
2836
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
@@ -2661,8 +2848,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2661
2848
|
applied: false,
|
|
2662
2849
|
data: fgaData({
|
|
2663
2850
|
action,
|
|
2851
|
+
workspaceRoot: options.workspaceRoot,
|
|
2664
2852
|
manifest,
|
|
2665
2853
|
state,
|
|
2854
|
+
seedState,
|
|
2666
2855
|
real,
|
|
2667
2856
|
...(cliAuth ? { cliAuth } : {}),
|
|
2668
2857
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
@@ -2688,8 +2877,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2688
2877
|
applied: false,
|
|
2689
2878
|
data: fgaData({
|
|
2690
2879
|
action,
|
|
2880
|
+
workspaceRoot: options.workspaceRoot,
|
|
2691
2881
|
manifest,
|
|
2692
2882
|
state,
|
|
2883
|
+
seedState,
|
|
2693
2884
|
real,
|
|
2694
2885
|
...(cliAuth ? { cliAuth } : {}),
|
|
2695
2886
|
workosSdk: sdk.data,
|
|
@@ -2718,8 +2909,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2718
2909
|
applied: true,
|
|
2719
2910
|
data: fgaData({
|
|
2720
2911
|
action,
|
|
2912
|
+
workspaceRoot: options.workspaceRoot,
|
|
2721
2913
|
manifest,
|
|
2722
2914
|
state,
|
|
2915
|
+
seedState,
|
|
2723
2916
|
real,
|
|
2724
2917
|
...(cliAuth ? { cliAuth } : {}),
|
|
2725
2918
|
...(sdk ? { workosSdk: sdk.data } : {}),
|
|
@@ -2789,8 +2982,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2789
2982
|
applied: Boolean(ok && !options.dryRun),
|
|
2790
2983
|
data: fgaData({
|
|
2791
2984
|
action,
|
|
2985
|
+
workspaceRoot: options.workspaceRoot,
|
|
2792
2986
|
manifest,
|
|
2793
2987
|
state,
|
|
2988
|
+
seedState,
|
|
2794
2989
|
real,
|
|
2795
2990
|
...(cliAuth ? { cliAuth } : {}),
|
|
2796
2991
|
...(sdk ? { workosSdk: sdk.data } : {}),
|
|
@@ -2804,20 +2999,40 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2804
2999
|
};
|
|
2805
3000
|
}
|
|
2806
3001
|
|
|
2807
|
-
const
|
|
3002
|
+
const doctorChecks = collectWorkOSFgaDoctorChecks({
|
|
3003
|
+
workspaceRoot: options.workspaceRoot,
|
|
3004
|
+
file,
|
|
3005
|
+
manifest,
|
|
3006
|
+
state,
|
|
3007
|
+
seedState,
|
|
3008
|
+
real,
|
|
3009
|
+
});
|
|
3010
|
+
const readiness = workOSFgaReadiness({
|
|
3011
|
+
workspaceRoot: options.workspaceRoot,
|
|
3012
|
+
file,
|
|
3013
|
+
manifest,
|
|
3014
|
+
state,
|
|
3015
|
+
seedState,
|
|
3016
|
+
real,
|
|
3017
|
+
});
|
|
3018
|
+
const ok = doctorChecks.every((check) => check.ok);
|
|
2808
3019
|
return {
|
|
2809
3020
|
ok,
|
|
2810
3021
|
kind: "workos-fga",
|
|
2811
|
-
checks,
|
|
3022
|
+
checks: doctorChecks,
|
|
2812
3023
|
command,
|
|
2813
3024
|
applied: false,
|
|
2814
3025
|
data: fgaData({
|
|
2815
3026
|
action,
|
|
3027
|
+
workspaceRoot: options.workspaceRoot,
|
|
2816
3028
|
manifest,
|
|
2817
3029
|
state,
|
|
3030
|
+
seedState,
|
|
3031
|
+
readiness,
|
|
2818
3032
|
real,
|
|
2819
3033
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
2820
|
-
nextCommand:
|
|
3034
|
+
nextCommand: readiness.nextCommand,
|
|
3035
|
+
nextActions: readiness.nextActions,
|
|
2821
3036
|
}),
|
|
2822
3037
|
exitCode: ok ? 0 : 1,
|
|
2823
3038
|
};
|
|
@@ -2953,6 +3168,8 @@ export function formatWorkOSHuman(result: WorkOSCommandResult): string {
|
|
|
2953
3168
|
lines.push(`WorkOS FGA sync recorded${data.stateFile ? ` in ${data.stateFile}` : ""}; run ${data.nextCommand ?? "forge workos fga prove --json"}.`);
|
|
2954
3169
|
} else if (result.ok && data.action === "prove") {
|
|
2955
3170
|
lines.push(`WorkOS FGA proof passed${data.real ? " for real-mode state" : " locally"}; production deploy gates can inspect the FGA state.`);
|
|
3171
|
+
} else if (result.ok && data.action === "doctor") {
|
|
3172
|
+
lines.push(`WorkOS FGA doctor passed${data.real ? " for real production gates" : " for local planning"}; run ${data.nextCommand ?? "forge workos fga sync --json"} if you need the next proof step.`);
|
|
2956
3173
|
} else if (!result.ok) {
|
|
2957
3174
|
lines.push("WorkOS FGA check failed; inspect manifest diagnostics, seed coverage, and FGA state.");
|
|
2958
3175
|
for (const action of data.nextActions ?? []) {
|
package/src/forge/version.ts
CHANGED