forgeos 0.1.0-alpha.50 → 0.1.0-alpha.51
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 +21 -0
- package/docs/changelog.md +17 -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 +213 -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.51 input=56e2fea9ee90281ea32b5a5f6297bf827b851718f02b6c41f1b3f6f862a7eca8 content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
|
|
2
2
|
# AGENTS.md
|
|
3
3
|
|
|
4
4
|
<!-- forge-generated:start -->
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# forgeos
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.51
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Make the WorkOS FGA production doctor explicit enough for real P2 rollout.
|
|
8
|
+
|
|
9
|
+
- `forge workos fga doctor --real --json` now reports structured readiness
|
|
10
|
+
for the full hosted path: FGA plan, hosted seed evidence, hosted resource
|
|
11
|
+
type/resource sync, `organizationMembershipId` env, real proof state, and
|
|
12
|
+
production readiness.
|
|
13
|
+
- FGA JSON output now includes `membershipEnv`, `seedState`, and `readiness`
|
|
14
|
+
metadata so agents can choose the next command without scraping human text.
|
|
15
|
+
- Hosted FGA setup data now states that WorkOS resource type creation is not
|
|
16
|
+
automated by the WorkOS API/CLI path, while resource sync and authorization
|
|
17
|
+
checks remain ForgeOS-driven after those resource types exist.
|
|
18
|
+
- WorkOS real checks now read `deploy/.env.production` as an explicit deploy
|
|
19
|
+
evidence source, and `forge deploy check --production` runs WorkOS/FGA
|
|
20
|
+
doctors with those values in scope.
|
|
21
|
+
- Human output now distinguishes local FGA planning success from real
|
|
22
|
+
production-gate readiness.
|
|
23
|
+
|
|
3
24
|
## 0.1.0-alpha.50
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/docs/changelog.md
CHANGED
|
@@ -6,6 +6,23 @@ The canonical source file in the repository is `CHANGELOG.md`.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.1.0-alpha.51
|
|
10
|
+
|
|
11
|
+
- `forge workos fga doctor --real --json` now reports a structured readiness
|
|
12
|
+
object for the full hosted FGA path: app-derived plan, hosted seed state,
|
|
13
|
+
real resource sync, membership env for WorkOS access checks, proof state, and
|
|
14
|
+
production readiness.
|
|
15
|
+
- FGA command JSON now includes `membershipEnv`, `seedState`, and `readiness`
|
|
16
|
+
metadata so agents can continue the no-dashboard WorkOS flow deterministically.
|
|
17
|
+
- Hosted FGA setup output now explicitly states that WorkOS resource type
|
|
18
|
+
creation is hosted WorkOS configuration, while ForgeOS can still sync
|
|
19
|
+
resources and run Authorization API checks after those types exist.
|
|
20
|
+
- WorkOS real checks now read `deploy/.env.production` as deploy evidence, and
|
|
21
|
+
`forge deploy check --production` runs WorkOS/FGA doctors with those values
|
|
22
|
+
in scope.
|
|
23
|
+
- Human FGA output now separates local planning success from real production
|
|
24
|
+
gate readiness.
|
|
25
|
+
|
|
9
26
|
## 0.1.0-alpha.50
|
|
10
27
|
|
|
11
28
|
- 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.51","releaseId":"forgeos@0.1.0-alpha.51+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.51 input=56e2fea9ee90281ea32b5a5f6297bf827b851718f02b6c41f1b3f6f862a7eca8 content=0e813cc871b8bd6a982fd4ccaf852bbc0a25e9dd49ab6a438de3bba9f0f840ab
|
|
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.51",
|
|
23
|
+
"releaseId": "forgeos@0.1.0-alpha.51+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: [
|
|
@@ -1179,11 +1221,14 @@ function writeWorkOSFgaSetupGuide(
|
|
|
1179
1221
|
|
|
1180
1222
|
function fgaData(input: {
|
|
1181
1223
|
action: WorkOSFgaAction;
|
|
1224
|
+
workspaceRoot: string;
|
|
1182
1225
|
manifest: WorkOSFgaManifest;
|
|
1183
1226
|
state: WorkOSFgaStateSummary;
|
|
1227
|
+
seedState?: WorkOSSeedStateSummary;
|
|
1184
1228
|
real?: boolean;
|
|
1185
1229
|
cliAuth?: WorkOSCliAuthSummary;
|
|
1186
1230
|
workosSdk?: unknown;
|
|
1231
|
+
readiness?: WorkOSFgaReadiness;
|
|
1187
1232
|
stateFile?: string;
|
|
1188
1233
|
nextCommand?: string;
|
|
1189
1234
|
nextActions?: string[];
|
|
@@ -1191,12 +1236,16 @@ function fgaData(input: {
|
|
|
1191
1236
|
}): Record<string, unknown> {
|
|
1192
1237
|
const hostedSetup = workOSFgaHostedSetup(input.manifest, input.workosSdk);
|
|
1193
1238
|
const setupGuide = workOSFgaSetupGuide(input.manifest, hostedSetup);
|
|
1239
|
+
const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
|
|
1194
1240
|
return {
|
|
1195
1241
|
action: input.action,
|
|
1196
1242
|
real: input.real ?? false,
|
|
1197
1243
|
manifest: input.manifest,
|
|
1198
1244
|
state: input.state,
|
|
1245
|
+
...(input.seedState ? { seedState: input.seedState } : {}),
|
|
1246
|
+
...(input.readiness ? { readiness: input.readiness } : {}),
|
|
1199
1247
|
hostedSetup,
|
|
1248
|
+
membershipEnv,
|
|
1200
1249
|
resourceTypeSetup: setupGuide.resourceTypes,
|
|
1201
1250
|
setupGuide,
|
|
1202
1251
|
...(input.cliAuth ? { cliAuth: input.cliAuth } : {}),
|
|
@@ -2573,6 +2622,133 @@ function collectWorkOSFgaChecks(input: {
|
|
|
2573
2622
|
];
|
|
2574
2623
|
}
|
|
2575
2624
|
|
|
2625
|
+
function workOSFgaReadiness(input: {
|
|
2626
|
+
workspaceRoot: string;
|
|
2627
|
+
file: string;
|
|
2628
|
+
manifest: WorkOSFgaManifest;
|
|
2629
|
+
state: WorkOSFgaStateSummary;
|
|
2630
|
+
seedState: WorkOSSeedStateSummary;
|
|
2631
|
+
real: boolean;
|
|
2632
|
+
}): WorkOSFgaReadiness {
|
|
2633
|
+
const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
|
|
2634
|
+
const planReady = input.manifest.diagnostics.length === 0 &&
|
|
2635
|
+
input.manifest.resourceTypes.includes("organization") &&
|
|
2636
|
+
input.manifest.proofScenarios.some((scenario) => scenario.expected === "allow") &&
|
|
2637
|
+
input.manifest.proofScenarios.some((scenario) => scenario.expected === "deny");
|
|
2638
|
+
const seedReady = !input.real || Boolean(input.seedState.exists && input.seedState.valid && input.seedState.matchesSeedHash === true);
|
|
2639
|
+
const synced = Boolean(input.state.exists && input.state.valid && input.state.matchesManifestHash === true);
|
|
2640
|
+
const resourceTypesConfigured = !input.real || Boolean(synced && input.state.mode === "real" && input.state.sdkOk === true);
|
|
2641
|
+
const membershipEnvReady = !input.real || membershipEnv.complete;
|
|
2642
|
+
const proved = !input.real
|
|
2643
|
+
? Boolean(input.state.provedAt)
|
|
2644
|
+
: Boolean(synced && input.state.mode === "real" && input.state.sdkOk === true && input.state.provedAt);
|
|
2645
|
+
const productionReady = Boolean(planReady && seedReady && resourceTypesConfigured && membershipEnvReady && proved);
|
|
2646
|
+
let nextCommand = `forge workos fga plan --file ${input.file} --write --json`;
|
|
2647
|
+
if (planReady && input.real && !seedReady) {
|
|
2648
|
+
nextCommand = `forge workos prove --real --file ${input.file} --json`;
|
|
2649
|
+
} else if (planReady && input.real && !resourceTypesConfigured) {
|
|
2650
|
+
nextCommand = `forge workos fga sync --real --file ${input.file} --write --json`;
|
|
2651
|
+
} else if (planReady && input.real && !membershipEnvReady) {
|
|
2652
|
+
nextCommand = `forge workos fga prove --real --file ${input.file} --json`;
|
|
2653
|
+
} else if (planReady && input.real && !proved) {
|
|
2654
|
+
nextCommand = `forge workos fga prove --real --file ${input.file} --json`;
|
|
2655
|
+
} else if (planReady && !input.real && !synced) {
|
|
2656
|
+
nextCommand = `forge workos fga sync --file ${input.file} --json`;
|
|
2657
|
+
} else if (planReady && !input.real && !proved) {
|
|
2658
|
+
nextCommand = `forge workos fga prove --file ${input.file} --json`;
|
|
2659
|
+
} else if (productionReady || (planReady && !input.real)) {
|
|
2660
|
+
nextCommand = "forge deploy check --production --json";
|
|
2661
|
+
}
|
|
2662
|
+
const nextActions = [
|
|
2663
|
+
...(planReady ? [] : [`repair FGA manifest gaps, then run forge workos fga plan --file ${input.file} --write --json`]),
|
|
2664
|
+
...(input.real && !seedReady ? [`apply/prove hosted WorkOS seed: forge workos prove --real --file ${input.file} --json`] : []),
|
|
2665
|
+
...(input.real && !resourceTypesConfigured
|
|
2666
|
+
? [
|
|
2667
|
+
"configure hosted WorkOS FGA resource types listed in resourceTypeSetup",
|
|
2668
|
+
`sync real WorkOS FGA resources: forge workos fga sync --real --file ${input.file} --write --json`,
|
|
2669
|
+
]
|
|
2670
|
+
: []),
|
|
2671
|
+
...(input.real && !membershipEnvReady
|
|
2672
|
+
? [`set WORKOS_FGA_MEMBERSHIPS_JSON or ${membershipEnv.missingEnv.join(", ") || "WORKOS_FGA_MEMBERSHIP_<ORG>"} before real access checks`]
|
|
2673
|
+
: []),
|
|
2674
|
+
...(input.real && membershipEnvReady && resourceTypesConfigured && !proved
|
|
2675
|
+
? [`prove real WorkOS FGA access checks: forge workos fga prove --real --file ${input.file} --json`]
|
|
2676
|
+
: []),
|
|
2677
|
+
...(productionReady ? ["rerun forge deploy check --production --json"] : []),
|
|
2678
|
+
...(!input.real && planReady && !synced ? [`run forge workos fga sync --file ${input.file} --json`] : []),
|
|
2679
|
+
...(!input.real && planReady && synced && !proved ? [`run forge workos fga prove --file ${input.file} --json`] : []),
|
|
2680
|
+
];
|
|
2681
|
+
return {
|
|
2682
|
+
real: input.real,
|
|
2683
|
+
planReady,
|
|
2684
|
+
seedReady,
|
|
2685
|
+
resourceTypesConfigured,
|
|
2686
|
+
membershipEnvReady,
|
|
2687
|
+
synced,
|
|
2688
|
+
proved,
|
|
2689
|
+
productionReady,
|
|
2690
|
+
nextCommand,
|
|
2691
|
+
nextActions,
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
function collectWorkOSFgaDoctorChecks(input: {
|
|
2696
|
+
workspaceRoot: string;
|
|
2697
|
+
file: string;
|
|
2698
|
+
manifest: WorkOSFgaManifest;
|
|
2699
|
+
state: WorkOSFgaStateSummary;
|
|
2700
|
+
seedState: WorkOSSeedStateSummary;
|
|
2701
|
+
real: boolean;
|
|
2702
|
+
}): WorkOSCheck[] {
|
|
2703
|
+
const readiness = workOSFgaReadiness(input);
|
|
2704
|
+
const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
|
|
2705
|
+
return [
|
|
2706
|
+
...collectWorkOSFgaChecks({
|
|
2707
|
+
manifest: input.manifest,
|
|
2708
|
+
state: input.state,
|
|
2709
|
+
requireState: input.real,
|
|
2710
|
+
requireRealState: input.real,
|
|
2711
|
+
requireProof: input.real,
|
|
2712
|
+
}),
|
|
2713
|
+
{
|
|
2714
|
+
name: "fga-seed-state",
|
|
2715
|
+
ok: !input.real || readiness.seedReady,
|
|
2716
|
+
detail: !input.real
|
|
2717
|
+
? "hosted seed evidence is only required for --real doctor"
|
|
2718
|
+
: readiness.seedReady
|
|
2719
|
+
? `${WORKOS_SEED_STATE_FILE} matches ${input.file}`
|
|
2720
|
+
: `real FGA proof requires hosted seed evidence matching ${input.file}; run forge workos prove --real --file ${input.file} --json`,
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
name: "fga-hosted-resource-types",
|
|
2724
|
+
ok: !input.real || readiness.resourceTypesConfigured,
|
|
2725
|
+
detail: !input.real
|
|
2726
|
+
? "hosted WorkOS resource type existence is only verified by --real sync/prove"
|
|
2727
|
+
: readiness.resourceTypesConfigured
|
|
2728
|
+
? `${WORKOS_FGA_STATE_FILE} records successful real Authorization API resource sync`
|
|
2729
|
+
: "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",
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
name: "fga-membership-env",
|
|
2733
|
+
ok: !input.real || readiness.membershipEnvReady,
|
|
2734
|
+
detail: !input.real
|
|
2735
|
+
? "organizationMembershipId env is only required for real WorkOS access checks"
|
|
2736
|
+
: readiness.membershipEnvReady
|
|
2737
|
+
? `membership env is present${membershipEnv.jsonEnvPresent ? " through WORKOS_FGA_MEMBERSHIPS_JSON" : ` through ${membershipEnv.presentEnv.join(", ")}`}`
|
|
2738
|
+
: `missing organizationMembershipId env for real checks: WORKOS_FGA_MEMBERSHIPS_JSON or ${membershipEnv.missingEnv.join(", ")}`,
|
|
2739
|
+
},
|
|
2740
|
+
{
|
|
2741
|
+
name: "fga-production-readiness",
|
|
2742
|
+
ok: !input.real || readiness.productionReady,
|
|
2743
|
+
detail: !input.real
|
|
2744
|
+
? "run forge workos fga doctor --real --json for production FGA gates"
|
|
2745
|
+
: readiness.productionReady
|
|
2746
|
+
? "real WorkOS FGA seed, resource sync, membership env, and proof are current"
|
|
2747
|
+
: `real WorkOS FGA is not production-ready; next command: ${readiness.nextCommand}`,
|
|
2748
|
+
},
|
|
2749
|
+
];
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2576
2752
|
export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSCommandResult {
|
|
2577
2753
|
const action = options.fgaAction ?? "doctor";
|
|
2578
2754
|
const file = options.file ?? DEFAULT_SEED_FILE;
|
|
@@ -2608,8 +2784,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2608
2784
|
applied: false,
|
|
2609
2785
|
data: fgaData({
|
|
2610
2786
|
action,
|
|
2787
|
+
workspaceRoot: options.workspaceRoot,
|
|
2611
2788
|
manifest,
|
|
2612
2789
|
state,
|
|
2790
|
+
seedState,
|
|
2613
2791
|
real,
|
|
2614
2792
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
2615
2793
|
nextCommand: `forge workos fga sync --file ${file} --json`,
|
|
@@ -2642,8 +2820,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2642
2820
|
applied: false,
|
|
2643
2821
|
data: fgaData({
|
|
2644
2822
|
action,
|
|
2823
|
+
workspaceRoot: options.workspaceRoot,
|
|
2645
2824
|
manifest,
|
|
2646
2825
|
state,
|
|
2826
|
+
seedState,
|
|
2647
2827
|
real,
|
|
2648
2828
|
...(cliAuth ? { cliAuth } : {}),
|
|
2649
2829
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
@@ -2661,8 +2841,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2661
2841
|
applied: false,
|
|
2662
2842
|
data: fgaData({
|
|
2663
2843
|
action,
|
|
2844
|
+
workspaceRoot: options.workspaceRoot,
|
|
2664
2845
|
manifest,
|
|
2665
2846
|
state,
|
|
2847
|
+
seedState,
|
|
2666
2848
|
real,
|
|
2667
2849
|
...(cliAuth ? { cliAuth } : {}),
|
|
2668
2850
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
@@ -2688,8 +2870,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2688
2870
|
applied: false,
|
|
2689
2871
|
data: fgaData({
|
|
2690
2872
|
action,
|
|
2873
|
+
workspaceRoot: options.workspaceRoot,
|
|
2691
2874
|
manifest,
|
|
2692
2875
|
state,
|
|
2876
|
+
seedState,
|
|
2693
2877
|
real,
|
|
2694
2878
|
...(cliAuth ? { cliAuth } : {}),
|
|
2695
2879
|
workosSdk: sdk.data,
|
|
@@ -2718,8 +2902,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2718
2902
|
applied: true,
|
|
2719
2903
|
data: fgaData({
|
|
2720
2904
|
action,
|
|
2905
|
+
workspaceRoot: options.workspaceRoot,
|
|
2721
2906
|
manifest,
|
|
2722
2907
|
state,
|
|
2908
|
+
seedState,
|
|
2723
2909
|
real,
|
|
2724
2910
|
...(cliAuth ? { cliAuth } : {}),
|
|
2725
2911
|
...(sdk ? { workosSdk: sdk.data } : {}),
|
|
@@ -2789,8 +2975,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2789
2975
|
applied: Boolean(ok && !options.dryRun),
|
|
2790
2976
|
data: fgaData({
|
|
2791
2977
|
action,
|
|
2978
|
+
workspaceRoot: options.workspaceRoot,
|
|
2792
2979
|
manifest,
|
|
2793
2980
|
state,
|
|
2981
|
+
seedState,
|
|
2794
2982
|
real,
|
|
2795
2983
|
...(cliAuth ? { cliAuth } : {}),
|
|
2796
2984
|
...(sdk ? { workosSdk: sdk.data } : {}),
|
|
@@ -2804,20 +2992,40 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
|
|
|
2804
2992
|
};
|
|
2805
2993
|
}
|
|
2806
2994
|
|
|
2807
|
-
const
|
|
2995
|
+
const doctorChecks = collectWorkOSFgaDoctorChecks({
|
|
2996
|
+
workspaceRoot: options.workspaceRoot,
|
|
2997
|
+
file,
|
|
2998
|
+
manifest,
|
|
2999
|
+
state,
|
|
3000
|
+
seedState,
|
|
3001
|
+
real,
|
|
3002
|
+
});
|
|
3003
|
+
const readiness = workOSFgaReadiness({
|
|
3004
|
+
workspaceRoot: options.workspaceRoot,
|
|
3005
|
+
file,
|
|
3006
|
+
manifest,
|
|
3007
|
+
state,
|
|
3008
|
+
seedState,
|
|
3009
|
+
real,
|
|
3010
|
+
});
|
|
3011
|
+
const ok = doctorChecks.every((check) => check.ok);
|
|
2808
3012
|
return {
|
|
2809
3013
|
ok,
|
|
2810
3014
|
kind: "workos-fga",
|
|
2811
|
-
checks,
|
|
3015
|
+
checks: doctorChecks,
|
|
2812
3016
|
command,
|
|
2813
3017
|
applied: false,
|
|
2814
3018
|
data: fgaData({
|
|
2815
3019
|
action,
|
|
3020
|
+
workspaceRoot: options.workspaceRoot,
|
|
2816
3021
|
manifest,
|
|
2817
3022
|
state,
|
|
3023
|
+
seedState,
|
|
3024
|
+
readiness,
|
|
2818
3025
|
real,
|
|
2819
3026
|
...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
|
|
2820
|
-
nextCommand:
|
|
3027
|
+
nextCommand: readiness.nextCommand,
|
|
3028
|
+
nextActions: readiness.nextActions,
|
|
2821
3029
|
}),
|
|
2822
3030
|
exitCode: ok ? 0 : 1,
|
|
2823
3031
|
};
|
|
@@ -2953,6 +3161,8 @@ export function formatWorkOSHuman(result: WorkOSCommandResult): string {
|
|
|
2953
3161
|
lines.push(`WorkOS FGA sync recorded${data.stateFile ? ` in ${data.stateFile}` : ""}; run ${data.nextCommand ?? "forge workos fga prove --json"}.`);
|
|
2954
3162
|
} else if (result.ok && data.action === "prove") {
|
|
2955
3163
|
lines.push(`WorkOS FGA proof passed${data.real ? " for real-mode state" : " locally"}; production deploy gates can inspect the FGA state.`);
|
|
3164
|
+
} else if (result.ok && data.action === "doctor") {
|
|
3165
|
+
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
3166
|
} else if (!result.ok) {
|
|
2957
3167
|
lines.push("WorkOS FGA check failed; inspect manifest diagnostics, seed coverage, and FGA state.");
|
|
2958
3168
|
for (const action of data.nextActions ?? []) {
|
package/src/forge/version.ts
CHANGED