eai-cli 0.0.0-bootstrap.0
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/LICENSE +183 -0
- package/NOTICE +4 -0
- package/README.md +14 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +69 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/blocks.d.ts +3 -0
- package/dist/commands/blocks.d.ts.map +1 -0
- package/dist/commands/blocks.js +249 -0
- package/dist/commands/blocks.js.map +1 -0
- package/dist/commands/chat.d.ts +6 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +105 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/deploy.d.ts +36 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +712 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +15 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +133 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/docs.d.ts +6 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +118 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/env.d.ts +10 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +200 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/errors.d.ts +3 -0
- package/dist/commands/errors.d.ts.map +1 -0
- package/dist/commands/errors.js +72 -0
- package/dist/commands/errors.js.map +1 -0
- package/dist/commands/gofer.d.ts +3 -0
- package/dist/commands/gofer.d.ts.map +1 -0
- package/dist/commands/gofer.js +134 -0
- package/dist/commands/gofer.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1437 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +117 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/provision.d.ts +6 -0
- package/dist/commands/provision.d.ts.map +1 -0
- package/dist/commands/provision.js +855 -0
- package/dist/commands/provision.js.map +1 -0
- package/dist/commands/publicapi.d.ts +6 -0
- package/dist/commands/publicapi.d.ts.map +1 -0
- package/dist/commands/publicapi.js +184 -0
- package/dist/commands/publicapi.js.map +1 -0
- package/dist/commands/resources.d.ts +38 -0
- package/dist/commands/resources.d.ts.map +1 -0
- package/dist/commands/resources.js +1010 -0
- package/dist/commands/resources.js.map +1 -0
- package/dist/commands/runtime.d.ts +3 -0
- package/dist/commands/runtime.d.ts.map +1 -0
- package/dist/commands/runtime.js +78 -0
- package/dist/commands/runtime.js.map +1 -0
- package/dist/commands/template.d.ts +3 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +365 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/tenant.d.ts +45 -0
- package/dist/commands/tenant.d.ts.map +1 -0
- package/dist/commands/tenant.js +1036 -0
- package/dist/commands/tenant.js.map +1 -0
- package/dist/commands/types.d.ts +130 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +1636 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +13 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +163 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/user.d.ts +6 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +359 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/commands/verify.d.ts +40 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +1442 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/vertical.d.ts +24 -0
- package/dist/commands/vertical.d.ts.map +1 -0
- package/dist/commands/vertical.js +301 -0
- package/dist/commands/vertical.js.map +1 -0
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +73 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/commands/workflow.d.ts +6 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +400 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent-guide.d.ts +24 -0
- package/dist/lib/agent-guide.d.ts.map +1 -0
- package/dist/lib/agent-guide.js +155 -0
- package/dist/lib/agent-guide.js.map +1 -0
- package/dist/lib/api.d.ts +338 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +991 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/auth.d.ts +65 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +592 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/azure-cli.d.ts +6 -0
- package/dist/lib/azure-cli.d.ts.map +1 -0
- package/dist/lib/azure-cli.js +13 -0
- package/dist/lib/azure-cli.js.map +1 -0
- package/dist/lib/block-catalog-normalize.d.ts +47 -0
- package/dist/lib/block-catalog-normalize.d.ts.map +1 -0
- package/dist/lib/block-catalog-normalize.js +355 -0
- package/dist/lib/block-catalog-normalize.js.map +1 -0
- package/dist/lib/block-catalog-types.d.ts +111 -0
- package/dist/lib/block-catalog-types.d.ts.map +1 -0
- package/dist/lib/block-catalog-types.js +41 -0
- package/dist/lib/block-catalog-types.js.map +1 -0
- package/dist/lib/block-catalog-utils.d.ts +7 -0
- package/dist/lib/block-catalog-utils.d.ts.map +1 -0
- package/dist/lib/block-catalog-utils.js +47 -0
- package/dist/lib/block-catalog-utils.js.map +1 -0
- package/dist/lib/block-catalog-validation.d.ts +5 -0
- package/dist/lib/block-catalog-validation.d.ts.map +1 -0
- package/dist/lib/block-catalog-validation.js +112 -0
- package/dist/lib/block-catalog-validation.js.map +1 -0
- package/dist/lib/block-catalog.d.ts +3 -0
- package/dist/lib/block-catalog.d.ts.map +1 -0
- package/dist/lib/block-catalog.js +284 -0
- package/dist/lib/block-catalog.js.map +1 -0
- package/dist/lib/cloud-env.d.ts +25 -0
- package/dist/lib/cloud-env.d.ts.map +1 -0
- package/dist/lib/cloud-env.js +79 -0
- package/dist/lib/cloud-env.js.map +1 -0
- package/dist/lib/config.d.ts +116 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +195 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/context.d.ts +43 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/context.js +68 -0
- package/dist/lib/context.js.map +1 -0
- package/dist/lib/error-codes.d.ts +56 -0
- package/dist/lib/error-codes.d.ts.map +1 -0
- package/dist/lib/error-codes.js +211 -0
- package/dist/lib/error-codes.js.map +1 -0
- package/dist/lib/error-guidance/catalog.d.ts +648 -0
- package/dist/lib/error-guidance/catalog.d.ts.map +1 -0
- package/dist/lib/error-guidance/catalog.js +940 -0
- package/dist/lib/error-guidance/catalog.js.map +1 -0
- package/dist/lib/error-guidance/match.d.ts +3 -0
- package/dist/lib/error-guidance/match.d.ts.map +1 -0
- package/dist/lib/error-guidance/match.js +46 -0
- package/dist/lib/error-guidance/match.js.map +1 -0
- package/dist/lib/error-guidance/render.d.ts +5 -0
- package/dist/lib/error-guidance/render.d.ts.map +1 -0
- package/dist/lib/error-guidance/render.js +92 -0
- package/dist/lib/error-guidance/render.js.map +1 -0
- package/dist/lib/error-guidance/types.d.ts +76 -0
- package/dist/lib/error-guidance/types.d.ts.map +1 -0
- package/dist/lib/error-guidance/types.js +2 -0
- package/dist/lib/error-guidance/types.js.map +1 -0
- package/dist/lib/error-guidance/validate.d.ts +3 -0
- package/dist/lib/error-guidance/validate.d.ts.map +1 -0
- package/dist/lib/error-guidance/validate.js +59 -0
- package/dist/lib/error-guidance/validate.js.map +1 -0
- package/dist/lib/gofer-installer.d.ts +32 -0
- package/dist/lib/gofer-installer.d.ts.map +1 -0
- package/dist/lib/gofer-installer.js +589 -0
- package/dist/lib/gofer-installer.js.map +1 -0
- package/dist/lib/gofer-refresh.d.ts +46 -0
- package/dist/lib/gofer-refresh.d.ts.map +1 -0
- package/dist/lib/gofer-refresh.js +536 -0
- package/dist/lib/gofer-refresh.js.map +1 -0
- package/dist/lib/next-route-exports.d.ts +6 -0
- package/dist/lib/next-route-exports.d.ts.map +1 -0
- package/dist/lib/next-route-exports.js +127 -0
- package/dist/lib/next-route-exports.js.map +1 -0
- package/dist/lib/npm.d.ts +2 -0
- package/dist/lib/npm.d.ts.map +1 -0
- package/dist/lib/npm.js +4 -0
- package/dist/lib/npm.js.map +1 -0
- package/dist/lib/object-type-defaults.d.ts +4 -0
- package/dist/lib/object-type-defaults.d.ts.map +1 -0
- package/dist/lib/object-type-defaults.js +72 -0
- package/dist/lib/object-type-defaults.js.map +1 -0
- package/dist/lib/output.d.ts +35 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +166 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/profile.d.ts +52 -0
- package/dist/lib/profile.d.ts.map +1 -0
- package/dist/lib/profile.js +116 -0
- package/dist/lib/profile.js.map +1 -0
- package/dist/lib/project-manifest.d.ts +42 -0
- package/dist/lib/project-manifest.d.ts.map +1 -0
- package/dist/lib/project-manifest.js +191 -0
- package/dist/lib/project-manifest.js.map +1 -0
- package/dist/lib/resourceapi-bundle.d.ts +33 -0
- package/dist/lib/resourceapi-bundle.d.ts.map +1 -0
- package/dist/lib/resourceapi-bundle.js +79 -0
- package/dist/lib/resourceapi-bundle.js.map +1 -0
- package/dist/lib/runtime-contract.d.ts +84 -0
- package/dist/lib/runtime-contract.d.ts.map +1 -0
- package/dist/lib/runtime-contract.js +318 -0
- package/dist/lib/runtime-contract.js.map +1 -0
- package/dist/lib/schema-builder.d.ts +28 -0
- package/dist/lib/schema-builder.d.ts.map +1 -0
- package/dist/lib/schema-builder.js +69 -0
- package/dist/lib/schema-builder.js.map +1 -0
- package/dist/lib/tenant-context.d.ts +127 -0
- package/dist/lib/tenant-context.d.ts.map +1 -0
- package/dist/lib/tenant-context.js +573 -0
- package/dist/lib/tenant-context.js.map +1 -0
- package/dist/lib/update-check.d.ts +52 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +271 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/update-maintenance.d.ts +15 -0
- package/dist/lib/update-maintenance.d.ts.map +1 -0
- package/dist/lib/update-maintenance.js +187 -0
- package/dist/lib/update-maintenance.js.map +1 -0
- package/dist/lib/utils.d.ts +23 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +48 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/workflow-provisioning.d.ts +54 -0
- package/dist/lib/workflow-provisioning.d.ts.map +1 -0
- package/dist/lib/workflow-provisioning.js +281 -0
- package/dist/lib/workflow-provisioning.js.map +1 -0
- package/package.json +51 -0
- package/resources/gofer/.gofer-version +7 -0
- package/resources/gofer/agents-skills/0_gofer_start/SKILL.md +1287 -0
- package/resources/gofer/agents-skills/0a_problem_validation/SKILL.md +429 -0
- package/resources/gofer/agents-skills/10_gofer_cloud/SKILL.md +635 -0
- package/resources/gofer/agents-skills/1_gofer_research/SKILL.md +1159 -0
- package/resources/gofer/agents-skills/2_gofer_specify/SKILL.md +860 -0
- package/resources/gofer/agents-skills/3_gofer_plan/SKILL.md +886 -0
- package/resources/gofer/agents-skills/4_gofer_tasks/SKILL.md +748 -0
- package/resources/gofer/agents-skills/5_gofer_implement/SKILL.md +924 -0
- package/resources/gofer/agents-skills/6_gofer_validate/SKILL.md +2269 -0
- package/resources/gofer/agents-skills/7_gofer_save/SKILL.md +423 -0
- package/resources/gofer/agents-skills/7a_stakeholder_comms/SKILL.md +508 -0
- package/resources/gofer/agents-skills/8_gofer_branding/SKILL.md +315 -0
- package/resources/gofer/agents-skills/9_gofer_tests/SKILL.md +558 -0
- package/resources/gofer/agents-skills/gofer_bootstrap_workspace/SKILL.md +148 -0
- package/resources/gofer/agents-skills/gofer_check_workspace/SKILL.md +119 -0
- package/resources/gofer/agents-skills/gofer_constitution/SKILL.md +612 -0
- package/resources/gofer/agents-skills/gofer_diagnose/SKILL.md +114 -0
- package/resources/gofer/agents-skills/gofer_eai_first_run/SKILL.md +397 -0
- package/resources/gofer/agents-skills/gofer_hydrate/SKILL.md +561 -0
- package/resources/gofer/agents-skills/gofer_personality/SKILL.md +44 -0
- package/resources/gofer/agents-skills/gofer_plan/SKILL.md +40 -0
- package/resources/gofer/agents-skills/gofer_side/SKILL.md +45 -0
- package/resources/gofer/agents-skills/gofer_spec_summary/SKILL.md +112 -0
- package/resources/gofer/agents-skills/gofer_tdd/SKILL.md +112 -0
- package/resources/gofer/agents-skills/gofer_vocabulary/SKILL.md +112 -0
- package/resources/gofer/agents-skills/gofer_zoom_out/SKILL.md +112 -0
- package/resources/gofer/bash-scripts/check-context-health.sh +365 -0
- package/resources/gofer/bash-scripts/check-persona-pack.sh +81 -0
- package/resources/gofer/bash-scripts/check-prerequisites.sh +168 -0
- package/resources/gofer/bash-scripts/common.sh +192 -0
- package/resources/gofer/bash-scripts/create-new-feature.sh +260 -0
- package/resources/gofer/bash-scripts/install-optional-tools.sh +270 -0
- package/resources/gofer/bash-scripts/log-stage.sh +224 -0
- package/resources/gofer/bash-scripts/mark-task-complete.sh +74 -0
- package/resources/gofer/bash-scripts/pipeline-state.sh +306 -0
- package/resources/gofer/bash-scripts/read-failed-approaches.sh +114 -0
- package/resources/gofer/bash-scripts/read-session-memories.sh +96 -0
- package/resources/gofer/bash-scripts/save-checkpoint.sh +407 -0
- package/resources/gofer/bash-scripts/setup-plan.sh +62 -0
- package/resources/gofer/bash-scripts/sync-implementation-status.sh +47 -0
- package/resources/gofer/bash-scripts/update-agent-context.sh +772 -0
- package/resources/gofer/bash-scripts/validate-artifact.sh +335 -0
- package/resources/gofer/bash-scripts/verify-task.sh +384 -0
- package/resources/gofer/bash-scripts/write-failed-approach.sh +110 -0
- package/resources/gofer/bash-scripts/write-periodic-checkpoint.sh +128 -0
- package/resources/gofer/bash-scripts/write-session-memory.sh +118 -0
- package/resources/gofer/claude-agents/assumption-tracker.md +168 -0
- package/resources/gofer/claude-agents/business-metrics-analyzer.md +169 -0
- package/resources/gofer/claude-agents/business-problem-validator.md +168 -0
- package/resources/gofer/claude-agents/codebase-analyzer.md +124 -0
- package/resources/gofer/claude-agents/codebase-locator.md +95 -0
- package/resources/gofer/claude-agents/codebase-pattern-finder.md +180 -0
- package/resources/gofer/claude-agents/comms-writer.md +158 -0
- package/resources/gofer/claude-agents/engineer-review.md +169 -0
- package/resources/gofer/claude-agents/implement-bug-triangulator.md +94 -0
- package/resources/gofer/claude-agents/implement-code-review-council.md +89 -0
- package/resources/gofer/claude-agents/implement-doc-writer.md +89 -0
- package/resources/gofer/claude-agents/implement-error-hardener.md +84 -0
- package/resources/gofer/claude-agents/implement-performance-explorer.md +95 -0
- package/resources/gofer/claude-agents/implement-test-diversifier.md +82 -0
- package/resources/gofer/claude-agents/implement-variant-generator.md +80 -0
- package/resources/gofer/claude-agents/multi-perspective-judge.md +114 -0
- package/resources/gofer/claude-agents/plan-api-comparator.md +90 -0
- package/resources/gofer/claude-agents/plan-architecture-diverger.md +100 -0
- package/resources/gofer/claude-agents/plan-data-model-stress-tester.md +95 -0
- package/resources/gofer/claude-agents/plan-migration-path-finder.md +95 -0
- package/resources/gofer/claude-agents/plan-refactor-rewrite-advisor.md +97 -0
- package/resources/gofer/claude-agents/research-dependency-evaluator.md +106 -0
- package/resources/gofer/claude-agents/research-horizon-scanner.md +100 -0
- package/resources/gofer/claude-agents/research-market-scanner.md +137 -0
- package/resources/gofer/claude-agents/research-perspective-multiplier.md +102 -0
- package/resources/gofer/claude-agents/scope-creep-detector.md +152 -0
- package/resources/gofer/claude-agents/specify-ambiguity-detector.md +95 -0
- package/resources/gofer/claude-agents/specify-journey-stress-tester.md +92 -0
- package/resources/gofer/claude-agents/tasks-cross-cutting-scanner.md +91 -0
- package/resources/gofer/claude-agents/tasks-rollback-planner.md +89 -0
- package/resources/gofer/claude-agents/validate-security-red-team.md +102 -0
- package/resources/gofer/claude-agents/validation-correctness.md +103 -0
- package/resources/gofer/claude-agents/validation-integration.md +129 -0
- package/resources/gofer/claude-agents/validation-performance.md +120 -0
- package/resources/gofer/claude-agents/validation-security.md +115 -0
- package/resources/gofer/claude-agents/validation-standards.md +151 -0
- package/resources/gofer/claude-agents/validation-test-quality.md +146 -0
- package/resources/gofer/claude-agents/visual-bounded-context-writer.md +40 -0
- package/resources/gofer/claude-agents/visual-c4-writer.md +45 -0
- package/resources/gofer/claude-agents/visual-erd-writer.md +34 -0
- package/resources/gofer/claude-agents/visual-heatmap-writer.md +63 -0
- package/resources/gofer/claude-agents/visual-risk-writer.md +64 -0
- package/resources/gofer/claude-commands/0_gofer_start.md +1282 -0
- package/resources/gofer/claude-commands/0a_problem_validation.md +424 -0
- package/resources/gofer/claude-commands/10_gofer_cloud.md +630 -0
- package/resources/gofer/claude-commands/1_gofer_research.md +1154 -0
- package/resources/gofer/claude-commands/2_gofer_specify.md +855 -0
- package/resources/gofer/claude-commands/3_gofer_plan.md +881 -0
- package/resources/gofer/claude-commands/4_gofer_tasks.md +743 -0
- package/resources/gofer/claude-commands/5_gofer_implement.md +919 -0
- package/resources/gofer/claude-commands/6_gofer_validate.md +2264 -0
- package/resources/gofer/claude-commands/7_gofer_save.md +418 -0
- package/resources/gofer/claude-commands/7a_stakeholder_comms.md +503 -0
- package/resources/gofer/claude-commands/8_gofer_branding.md +310 -0
- package/resources/gofer/claude-commands/9_gofer_tests.md +553 -0
- package/resources/gofer/claude-commands/gofer_bootstrap_workspace.md +143 -0
- package/resources/gofer/claude-commands/gofer_check_workspace.md +114 -0
- package/resources/gofer/claude-commands/gofer_constitution.md +607 -0
- package/resources/gofer/claude-commands/gofer_diagnose.md +109 -0
- package/resources/gofer/claude-commands/gofer_eai_first_run.md +392 -0
- package/resources/gofer/claude-commands/gofer_hydrate.md +556 -0
- package/resources/gofer/claude-commands/gofer_personality.md +39 -0
- package/resources/gofer/claude-commands/gofer_plan.md +35 -0
- package/resources/gofer/claude-commands/gofer_side.md +40 -0
- package/resources/gofer/claude-commands/gofer_spec_summary.md +107 -0
- package/resources/gofer/claude-commands/gofer_tdd.md +107 -0
- package/resources/gofer/claude-commands/gofer_vocabulary.md +107 -0
- package/resources/gofer/claude-commands/gofer_zoom_out.md +107 -0
- package/resources/gofer/claude-skills/eai-gofer/SKILL.md +56 -0
- package/resources/gofer/commands/.gitkeep +0 -0
- package/resources/gofer/commands/0_gofer_start.md +1299 -0
- package/resources/gofer/commands/0a_problem_validation.md +405 -0
- package/resources/gofer/commands/10_gofer_cloud.md +611 -0
- package/resources/gofer/commands/1_gofer_research.md +1135 -0
- package/resources/gofer/commands/2_gofer_specify.md +836 -0
- package/resources/gofer/commands/3_gofer_plan.md +862 -0
- package/resources/gofer/commands/4_gofer_tasks.md +724 -0
- package/resources/gofer/commands/5_gofer_implement.md +900 -0
- package/resources/gofer/commands/6_gofer_validate.md +2245 -0
- package/resources/gofer/commands/7_gofer_save.md +399 -0
- package/resources/gofer/commands/7a_stakeholder_comms.md +484 -0
- package/resources/gofer/commands/8_gofer_branding.md +291 -0
- package/resources/gofer/commands/9_gofer_tests.md +534 -0
- package/resources/gofer/commands/gofer_bootstrap_workspace.md +159 -0
- package/resources/gofer/commands/gofer_check_workspace.md +130 -0
- package/resources/gofer/commands/gofer_constitution.md +588 -0
- package/resources/gofer/commands/gofer_diagnose.md +89 -0
- package/resources/gofer/commands/gofer_eai_first_run.md +392 -0
- package/resources/gofer/commands/gofer_hydrate.md +537 -0
- package/resources/gofer/commands/gofer_personality.md +61 -0
- package/resources/gofer/commands/gofer_plan.md +51 -0
- package/resources/gofer/commands/gofer_side.md +56 -0
- package/resources/gofer/commands/gofer_spec_summary.md +87 -0
- package/resources/gofer/commands/gofer_tdd.md +87 -0
- package/resources/gofer/commands/gofer_vocabulary.md +87 -0
- package/resources/gofer/commands/gofer_zoom_out.md +87 -0
- package/resources/gofer/copilot-instructions/gofer-cost.instructions.md +13 -0
- package/resources/gofer/copilot-instructions/language-server.instructions.md +49 -0
- package/resources/gofer/copilot-instructions/typescript.instructions.md +47 -0
- package/resources/gofer/copilot-instructions/vscode-extension.instructions.md +46 -0
- package/resources/gofer/copilot-prompts/0_gofer_start.prompt.md +1308 -0
- package/resources/gofer/copilot-prompts/0a_problem_validation.prompt.md +429 -0
- package/resources/gofer/copilot-prompts/10_gofer_cloud.prompt.md +644 -0
- package/resources/gofer/copilot-prompts/1_gofer_research.prompt.md +1177 -0
- package/resources/gofer/copilot-prompts/2_gofer_specify.prompt.md +878 -0
- package/resources/gofer/copilot-prompts/3_gofer_plan.prompt.md +904 -0
- package/resources/gofer/copilot-prompts/4_gofer_tasks.prompt.md +766 -0
- package/resources/gofer/copilot-prompts/5_gofer_implement.prompt.md +942 -0
- package/resources/gofer/copilot-prompts/6_gofer_validate.prompt.md +2277 -0
- package/resources/gofer/copilot-prompts/7_gofer_save.prompt.md +433 -0
- package/resources/gofer/copilot-prompts/7a_stakeholder_comms.prompt.md +516 -0
- package/resources/gofer/copilot-prompts/8_gofer_branding.prompt.md +323 -0
- package/resources/gofer/copilot-prompts/9_gofer_tests.prompt.md +566 -0
- package/resources/gofer/copilot-prompts/gofer_bootstrap_workspace.prompt.md +161 -0
- package/resources/gofer/copilot-prompts/gofer_check_workspace.prompt.md +132 -0
- package/resources/gofer/copilot-prompts/gofer_constitution.prompt.md +621 -0
- package/resources/gofer/copilot-prompts/gofer_diagnose.prompt.md +127 -0
- package/resources/gofer/copilot-prompts/gofer_eai_first_run.prompt.md +410 -0
- package/resources/gofer/copilot-prompts/gofer_hydrate.prompt.md +571 -0
- package/resources/gofer/copilot-prompts/gofer_personality.prompt.md +57 -0
- package/resources/gofer/copilot-prompts/gofer_plan.prompt.md +53 -0
- package/resources/gofer/copilot-prompts/gofer_side.prompt.md +58 -0
- package/resources/gofer/copilot-prompts/gofer_spec_summary.prompt.md +125 -0
- package/resources/gofer/copilot-prompts/gofer_tdd.prompt.md +125 -0
- package/resources/gofer/copilot-prompts/gofer_vocabulary.prompt.md +125 -0
- package/resources/gofer/copilot-prompts/gofer_zoom_out.prompt.md +125 -0
- package/resources/gofer/gemini/agents/assumption-tracker.md +171 -0
- package/resources/gofer/gemini/agents/business-metrics-analyzer.md +172 -0
- package/resources/gofer/gemini/agents/business-problem-validator.md +171 -0
- package/resources/gofer/gemini/agents/codebase-analyzer.md +127 -0
- package/resources/gofer/gemini/agents/codebase-locator.md +98 -0
- package/resources/gofer/gemini/agents/codebase-pattern-finder.md +183 -0
- package/resources/gofer/gemini/agents/comms-writer.md +161 -0
- package/resources/gofer/gemini/agents/engineer-review.md +172 -0
- package/resources/gofer/gemini/agents/implement-bug-triangulator.md +97 -0
- package/resources/gofer/gemini/agents/implement-code-review-council.md +92 -0
- package/resources/gofer/gemini/agents/implement-doc-writer.md +92 -0
- package/resources/gofer/gemini/agents/implement-error-hardener.md +87 -0
- package/resources/gofer/gemini/agents/implement-performance-explorer.md +98 -0
- package/resources/gofer/gemini/agents/implement-test-diversifier.md +85 -0
- package/resources/gofer/gemini/agents/implement-variant-generator.md +83 -0
- package/resources/gofer/gemini/agents/multi-perspective-judge.md +117 -0
- package/resources/gofer/gemini/agents/plan-api-comparator.md +93 -0
- package/resources/gofer/gemini/agents/plan-architecture-diverger.md +103 -0
- package/resources/gofer/gemini/agents/plan-data-model-stress-tester.md +98 -0
- package/resources/gofer/gemini/agents/plan-migration-path-finder.md +98 -0
- package/resources/gofer/gemini/agents/plan-refactor-rewrite-advisor.md +100 -0
- package/resources/gofer/gemini/agents/research-dependency-evaluator.md +109 -0
- package/resources/gofer/gemini/agents/research-horizon-scanner.md +103 -0
- package/resources/gofer/gemini/agents/research-market-scanner.md +140 -0
- package/resources/gofer/gemini/agents/research-perspective-multiplier.md +105 -0
- package/resources/gofer/gemini/agents/scope-creep-detector.md +155 -0
- package/resources/gofer/gemini/agents/specify-ambiguity-detector.md +98 -0
- package/resources/gofer/gemini/agents/specify-journey-stress-tester.md +95 -0
- package/resources/gofer/gemini/agents/tasks-cross-cutting-scanner.md +94 -0
- package/resources/gofer/gemini/agents/tasks-rollback-planner.md +92 -0
- package/resources/gofer/gemini/agents/validate-security-red-team.md +105 -0
- package/resources/gofer/gemini/agents/validation-correctness.md +106 -0
- package/resources/gofer/gemini/agents/validation-integration.md +132 -0
- package/resources/gofer/gemini/agents/validation-performance.md +123 -0
- package/resources/gofer/gemini/agents/validation-security.md +118 -0
- package/resources/gofer/gemini/agents/validation-standards.md +154 -0
- package/resources/gofer/gemini/agents/validation-test-quality.md +149 -0
- package/resources/gofer/gemini/agents/visual-bounded-context-writer.md +40 -0
- package/resources/gofer/gemini/agents/visual-c4-writer.md +44 -0
- package/resources/gofer/gemini/agents/visual-erd-writer.md +36 -0
- package/resources/gofer/gemini/agents/visual-heatmap-writer.md +63 -0
- package/resources/gofer/gemini/agents/visual-risk-writer.md +64 -0
- package/resources/gofer/gemini/commands/gofer/.gitkeep +0 -0
- package/resources/gofer/gemini/commands/gofer/0_gofer_start.md +1282 -0
- package/resources/gofer/gemini/commands/gofer/0_gofer_start.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/0a_problem_validation.md +424 -0
- package/resources/gofer/gemini/commands/gofer/0a_problem_validation.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.md +630 -0
- package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/1_gofer_research.md +1154 -0
- package/resources/gofer/gemini/commands/gofer/1_gofer_research.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/2_gofer_specify.md +855 -0
- package/resources/gofer/gemini/commands/gofer/2_gofer_specify.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/3_gofer_plan.md +881 -0
- package/resources/gofer/gemini/commands/gofer/3_gofer_plan.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.md +743 -0
- package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/5_gofer_implement.md +919 -0
- package/resources/gofer/gemini/commands/gofer/5_gofer_implement.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/6_gofer_validate.md +2264 -0
- package/resources/gofer/gemini/commands/gofer/6_gofer_validate.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/7_gofer_save.md +418 -0
- package/resources/gofer/gemini/commands/gofer/7_gofer_save.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.md +503 -0
- package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/8_gofer_branding.md +310 -0
- package/resources/gofer/gemini/commands/gofer/8_gofer_branding.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/9_gofer_tests.md +553 -0
- package/resources/gofer/gemini/commands/gofer/9_gofer_tests.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.md +143 -0
- package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.md +114 -0
- package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_constitution.md +607 -0
- package/resources/gofer/gemini/commands/gofer/gofer_constitution.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_diagnose.md +109 -0
- package/resources/gofer/gemini/commands/gofer/gofer_diagnose.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.md +392 -0
- package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_hydrate.md +556 -0
- package/resources/gofer/gemini/commands/gofer/gofer_hydrate.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_personality.md +39 -0
- package/resources/gofer/gemini/commands/gofer/gofer_personality.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_plan.md +35 -0
- package/resources/gofer/gemini/commands/gofer/gofer_plan.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_side.md +40 -0
- package/resources/gofer/gemini/commands/gofer/gofer_side.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.md +107 -0
- package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_tdd.md +107 -0
- package/resources/gofer/gemini/commands/gofer/gofer_tdd.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.md +107 -0
- package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.md +107 -0
- package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/manifest.json +32 -0
- package/resources/gofer/gemini/extension.json +16 -0
- package/resources/gofer/github-agents/gofer-business.agent.md +20 -0
- package/resources/gofer/github-agents/gofer-implement.agent.md +15 -0
- package/resources/gofer/github-agents/gofer-plan.agent.md +15 -0
- package/resources/gofer/github-agents/gofer-research.agent.md +15 -0
- package/resources/gofer/github-agents/gofer-validate.agent.md +10 -0
- package/resources/gofer/github-skills/eai-gofer/SKILL.md +56 -0
- package/resources/gofer/hook-scripts/agent-stop.mjs +655 -0
- package/resources/gofer/hook-scripts/log-stage-launch-time.mjs +77 -0
- package/resources/gofer/hook-scripts/post-tool-use.mjs +333 -0
- package/resources/gofer/hook-scripts/queued-input.mjs +188 -0
- package/resources/gofer/hook-scripts/session-lifecycle.mjs +93 -0
- package/resources/gofer/hook-scripts/user-prompt-submit.mjs +228 -0
- package/resources/gofer/instruction-templates/base/agents-base.md +37 -0
- package/resources/gofer/instruction-templates/base/claude-base.md +7 -0
- package/resources/gofer/instruction-templates/base/copilot-base.md +26 -0
- package/resources/gofer/instruction-templates/gofer/gofer-claude.md +5 -0
- package/resources/gofer/instruction-templates/gofer/gofer-copilot.md +9 -0
- package/resources/gofer/instruction-templates/languages/generic.md +7 -0
- package/resources/gofer/instruction-templates/languages/go.md +8 -0
- package/resources/gofer/instruction-templates/languages/java.md +8 -0
- package/resources/gofer/instruction-templates/languages/python.md +9 -0
- package/resources/gofer/instruction-templates/languages/rust.md +9 -0
- package/resources/gofer/instruction-templates/languages/typescript.md +19 -0
- package/resources/gofer/instruction-templates/workflow/principles.md +52 -0
- package/resources/gofer/memory/constitution.md +391 -0
- package/resources/gofer/memory/context-profiles.yaml +74 -0
- package/resources/gofer/memory/decisions/000-use-adr-for-decisions.md +67 -0
- package/resources/gofer/memory/decisions/001-di-framework.md +190 -0
- package/resources/gofer/memory/decisions/002-module-extraction.md +305 -0
- package/resources/gofer/memory/decisions/003-error-handling.md +368 -0
- package/resources/gofer/memory/decisions/004-cache-eviction.md +436 -0
- package/resources/gofer/memory/decisions/005-constants-management.md +520 -0
- package/resources/gofer/memory/decisions/README.md +129 -0
- package/resources/gofer/memory/diagrams/di-container.mmd +321 -0
- package/resources/gofer/memory/diagrams/extension-activation.mmd +184 -0
- package/resources/gofer/memory/diagrams/module-dependencies.mmd +264 -0
- package/resources/gofer/memory/gofer-model-policy.yaml +97 -0
- package/resources/gofer/memory/lessons.md +46 -0
- package/resources/gofer/node-scripts/canonical-descriptions.mjs +86 -0
- package/resources/gofer/node-scripts/check-version-alignment.mjs +131 -0
- package/resources/gofer/node-scripts/codex-doctor.mjs +425 -0
- package/resources/gofer/node-scripts/generate-commands.mjs +1366 -0
- package/resources/gofer/node-scripts/generate-issues.js +563 -0
- package/resources/gofer/node-scripts/gofer-closed-loop-audit.mjs +893 -0
- package/resources/gofer/node-scripts/gofer-loop-audit.mjs +541 -0
- package/resources/gofer/node-scripts/gofer-performance-report.mjs +306 -0
- package/resources/gofer/node-scripts/gofer-workspace-bootstrap.mjs +56 -0
- package/resources/gofer/node-scripts/gofer-workspace-check.mjs +56 -0
- package/resources/gofer/node-scripts/lib/ai-leverage-tagger.mjs +103 -0
- package/resources/gofer/node-scripts/lib/assemble-stakeholder-pack.mjs +114 -0
- package/resources/gofer/node-scripts/lib/marp-deck.mjs +87 -0
- package/resources/gofer/node-scripts/lib/mermaid-tabular-fallback.mjs +264 -0
- package/resources/gofer/node-scripts/lib/render-visual.mjs +54 -0
- package/resources/gofer/node-scripts/lib/validate-aliases.mjs +85 -0
- package/resources/gofer/node-scripts/lib/visual-counts.mjs +44 -0
- package/resources/gofer/node-scripts/lib/visual-pass-pipeline.mjs +134 -0
- package/resources/gofer/node-scripts/mermaid-export.mjs +72 -0
- package/resources/gofer/node-scripts/package-agent-plugin.mjs +940 -0
- package/resources/gofer/node-scripts/parse-stage-command.mjs +211 -0
- package/resources/gofer/node-scripts/schemas/.gitkeep +0 -0
- package/resources/gofer/node-scripts/schemas/stage-command.schema.json +72 -0
- package/resources/gofer/node-scripts/sync-extension-resources.mjs +110 -0
- package/resources/gofer/node-scripts/workspace-bootstrap-lib.mjs +1166 -0
- package/resources/gofer/powershell-scripts/install-optional-tools.ps1 +214 -0
- package/resources/gofer/references/platform/README.md +17 -0
- package/resources/gofer/references/platform/deployment-repo.md +9 -0
- package/resources/gofer/references/platform/eai-app-template.md +45 -0
- package/resources/gofer/references/platform/eai-config-driven-ui.md +156 -0
- package/resources/gofer/references/platform/eai-error-catalog.yaml +197 -0
- package/resources/gofer/references/platform/eai-repo-contract.md +134 -0
- package/resources/gofer/references/platform/eai-service-patterns.md +56 -0
- package/resources/gofer/references/platform/eai.md +85 -0
- package/resources/gofer/references/platform/vertical-template.md +49 -0
- package/resources/gofer/specify-commands/.gitkeep +0 -0
- package/resources/gofer/specify-commands/0_gofer_start.md +1299 -0
- package/resources/gofer/specify-commands/0a_problem_validation.md +405 -0
- package/resources/gofer/specify-commands/10_gofer_cloud.md +611 -0
- package/resources/gofer/specify-commands/1_gofer_research.md +1135 -0
- package/resources/gofer/specify-commands/2_gofer_specify.md +836 -0
- package/resources/gofer/specify-commands/3_gofer_plan.md +862 -0
- package/resources/gofer/specify-commands/4_gofer_tasks.md +724 -0
- package/resources/gofer/specify-commands/5_gofer_implement.md +900 -0
- package/resources/gofer/specify-commands/6_gofer_validate.md +2245 -0
- package/resources/gofer/specify-commands/7_gofer_save.md +399 -0
- package/resources/gofer/specify-commands/7a_stakeholder_comms.md +484 -0
- package/resources/gofer/specify-commands/8_gofer_branding.md +291 -0
- package/resources/gofer/specify-commands/9_gofer_tests.md +534 -0
- package/resources/gofer/specify-commands/gofer_bootstrap_workspace.md +159 -0
- package/resources/gofer/specify-commands/gofer_check_workspace.md +130 -0
- package/resources/gofer/specify-commands/gofer_constitution.md +588 -0
- package/resources/gofer/specify-commands/gofer_diagnose.md +89 -0
- package/resources/gofer/specify-commands/gofer_eai_first_run.md +392 -0
- package/resources/gofer/specify-commands/gofer_hydrate.md +537 -0
- package/resources/gofer/specify-commands/gofer_personality.md +61 -0
- package/resources/gofer/specify-commands/gofer_plan.md +51 -0
- package/resources/gofer/specify-commands/gofer_side.md +56 -0
- package/resources/gofer/specify-commands/gofer_spec_summary.md +87 -0
- package/resources/gofer/specify-commands/gofer_tdd.md +87 -0
- package/resources/gofer/specify-commands/gofer_vocabulary.md +87 -0
- package/resources/gofer/specify-commands/gofer_zoom_out.md +87 -0
- package/resources/gofer/system-skills/0_gofer_start/SKILL.md +1287 -0
- package/resources/gofer/system-skills/0a_problem_validation/SKILL.md +429 -0
- package/resources/gofer/system-skills/10_gofer_cloud/SKILL.md +635 -0
- package/resources/gofer/system-skills/1_gofer_research/SKILL.md +1159 -0
- package/resources/gofer/system-skills/2_gofer_specify/SKILL.md +860 -0
- package/resources/gofer/system-skills/3_gofer_plan/SKILL.md +886 -0
- package/resources/gofer/system-skills/4_gofer_tasks/SKILL.md +748 -0
- package/resources/gofer/system-skills/5_gofer_implement/SKILL.md +924 -0
- package/resources/gofer/system-skills/6_gofer_validate/SKILL.md +2269 -0
- package/resources/gofer/system-skills/7_gofer_save/SKILL.md +423 -0
- package/resources/gofer/system-skills/7a_stakeholder_comms/SKILL.md +508 -0
- package/resources/gofer/system-skills/8_gofer_branding/SKILL.md +315 -0
- package/resources/gofer/system-skills/9_gofer_tests/SKILL.md +558 -0
- package/resources/gofer/system-skills/gofer_bootstrap_workspace/SKILL.md +148 -0
- package/resources/gofer/system-skills/gofer_check_workspace/SKILL.md +119 -0
- package/resources/gofer/system-skills/gofer_constitution/SKILL.md +612 -0
- package/resources/gofer/system-skills/gofer_diagnose/SKILL.md +114 -0
- package/resources/gofer/system-skills/gofer_eai_first_run/SKILL.md +397 -0
- package/resources/gofer/system-skills/gofer_hydrate/SKILL.md +561 -0
- package/resources/gofer/system-skills/gofer_personality/SKILL.md +44 -0
- package/resources/gofer/system-skills/gofer_plan/SKILL.md +40 -0
- package/resources/gofer/system-skills/gofer_side/SKILL.md +45 -0
- package/resources/gofer/system-skills/gofer_spec_summary/SKILL.md +112 -0
- package/resources/gofer/system-skills/gofer_tdd/SKILL.md +112 -0
- package/resources/gofer/system-skills/gofer_vocabulary/SKILL.md +112 -0
- package/resources/gofer/system-skills/gofer_zoom_out/SKILL.md +112 -0
- package/resources/gofer/templates/agent-file-template.md +28 -0
- package/resources/gofer/templates/assumptions-template.md +94 -0
- package/resources/gofer/templates/audit-history-template.md +35 -0
- package/resources/gofer/templates/brand/brand-profile-template.json +45 -0
- package/resources/gofer/templates/brand/document-style-template.md +50 -0
- package/resources/gofer/templates/brand/marp-theme-template.css +58 -0
- package/resources/gofer/templates/brownfield-analysis.md +44 -0
- package/resources/gofer/templates/business-metrics-template.md +120 -0
- package/resources/gofer/templates/business-owner-summary-template.md +83 -0
- package/resources/gofer/templates/checklist-template.md +40 -0
- package/resources/gofer/templates/ciso-security-summary-template.md +70 -0
- package/resources/gofer/templates/context-bundle-template.md +76 -0
- package/resources/gofer/templates/contract-pack-template.md +109 -0
- package/resources/gofer/templates/cto-architecture-summary-template.md +83 -0
- package/resources/gofer/templates/discovery-template.md +92 -0
- package/resources/gofer/templates/eai-preflight-template.md +144 -0
- package/resources/gofer/templates/goal-ledger-template.json +69 -0
- package/resources/gofer/templates/gofer-model-policy.yaml +97 -0
- package/resources/gofer/templates/issues-template.md +106 -0
- package/resources/gofer/templates/journey/base-journey.md +95 -0
- package/resources/gofer/templates/journey/industry-variants.yaml +277 -0
- package/resources/gofer/templates/loop-contract-template.json +68 -0
- package/resources/gofer/templates/plan-template.md +215 -0
- package/resources/gofer/templates/problem-brief-template.md +85 -0
- package/resources/gofer/templates/proposal-review-template.md +79 -0
- package/resources/gofer/templates/research-template.md +222 -0
- package/resources/gofer/templates/reuse-scan-template.md +43 -0
- package/resources/gofer/templates/sequence-diagrams/option-spectrum.yaml +178 -0
- package/resources/gofer/templates/service-fit-matrix-template.md +29 -0
- package/resources/gofer/templates/session-handoff-template.md +172 -0
- package/resources/gofer/templates/spec-summary-template.md +118 -0
- package/resources/gofer/templates/spec-template.md +196 -0
- package/resources/gofer/templates/stakeholder-comms-template.md +330 -0
- package/resources/gofer/templates/stakeholder-review-index-template.md +54 -0
- package/resources/gofer/templates/tasks-template.md +323 -0
- package/resources/gofer/templates/ui-approval-template.md +32 -0
- package/resources/gofer/templates/ui-preview-brief-template.md +65 -0
- package/resources/gofer/templates/ui-review-log-template.md +11 -0
- package/resources/gofer/templates/visuals/.gitkeep +0 -0
- package/resources/gofer/templates/visuals/bounded-context-map.md +19 -0
- package/resources/gofer/templates/visuals/bounded-context-template.md +47 -0
- package/resources/gofer/templates/visuals/c4-container-template.md +38 -0
- package/resources/gofer/templates/visuals/c4-container.md +20 -0
- package/resources/gofer/templates/visuals/c4-context-template.md +46 -0
- package/resources/gofer/templates/visuals/capability-heatmap-template.md +54 -0
- package/resources/gofer/templates/visuals/data-model-erd-template.md +41 -0
- package/resources/gofer/templates/visuals/erd.md +19 -0
- package/resources/gofer/templates/visuals/impact-canvas.md +40 -0
- package/resources/gofer/templates/visuals/risk-heatmap-template.md +51 -0
- package/resources/gofer/templates/visuals/roi-projection.md +17 -0
- package/resources/gofer/templates/visuals/value-stream-asis.md +16 -0
- package/resources/gofer/templates/visuals/value-stream-tobe.md +18 -0
- package/resources/gofer/templates/working-backwards-prfaq-template.md +165 -0
- package/resources/linked-sources.json +25 -0
|
@@ -0,0 +1,1036 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eai tenant — manage tenants on the platform.
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
|
+
import { findProjectRoot } from "../lib/config.js";
|
|
9
|
+
import { PlatformAPIClient, parseApiError, } from "../lib/api.js";
|
|
10
|
+
import { loadTokens } from "../lib/auth.js";
|
|
11
|
+
import { buildPublicApiEnvSyncNotice, fetchTenantAdminMemberships, normalizeHomeRegion, refreshTenantUsabilityStatus, resolveActiveTenantContext, resolvePublicApiUrl, } from "../lib/tenant-context.js";
|
|
12
|
+
import * as out from "../lib/output.js";
|
|
13
|
+
import { ErrorCode, exitWithError } from "../lib/error-codes.js";
|
|
14
|
+
export { filterTenantAdminEntries, tenantEntryHasTenantAdminRole, } from "../lib/tenant-context.js";
|
|
15
|
+
export function tenantMatchesParent(entry, parentId) {
|
|
16
|
+
const parent = entry.tenant.parent;
|
|
17
|
+
const resolvedParentId = typeof parent === "string" ? parent : (parent?.id ?? entry.tenant.parentId);
|
|
18
|
+
return resolvedParentId === parentId || entry.tenant.id === parentId;
|
|
19
|
+
}
|
|
20
|
+
const HOME_REGION_CHOICES = [
|
|
21
|
+
{ name: "Australia / New Zealand (au)", value: "au" },
|
|
22
|
+
{ name: "Canada / Americas (ca)", value: "ca" },
|
|
23
|
+
{ name: "Europe / UK (eu)", value: "eu" },
|
|
24
|
+
];
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function optionalString(value) {
|
|
29
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
30
|
+
}
|
|
31
|
+
function tenantReferenceId(value) {
|
|
32
|
+
if (typeof value === "string" && value.trim())
|
|
33
|
+
return value;
|
|
34
|
+
if (!isRecord(value))
|
|
35
|
+
return null;
|
|
36
|
+
return optionalString(value.id) ?? optionalString(value._id) ?? null;
|
|
37
|
+
}
|
|
38
|
+
function tenantParentIdFromRecord(record) {
|
|
39
|
+
return (tenantReferenceId(record.parentTenant) ??
|
|
40
|
+
tenantReferenceId(record.parent) ??
|
|
41
|
+
optionalString(record.parentTenantId) ??
|
|
42
|
+
optionalString(record.parentId) ??
|
|
43
|
+
optionalString(record.parent_tenant_id) ??
|
|
44
|
+
null);
|
|
45
|
+
}
|
|
46
|
+
function tenantRecordsFromPayload(payload) {
|
|
47
|
+
if (Array.isArray(payload)) {
|
|
48
|
+
return payload.filter(isRecord);
|
|
49
|
+
}
|
|
50
|
+
if (!isRecord(payload))
|
|
51
|
+
return [];
|
|
52
|
+
for (const key of ["children", "tenants", "docs", "items", "data"]) {
|
|
53
|
+
const value = payload[key];
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
return value.filter(isRecord);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (isRecord(payload.tenant)) {
|
|
59
|
+
return [payload.tenant];
|
|
60
|
+
}
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
function tenantHierarchyItemFromMembership(membership) {
|
|
64
|
+
return {
|
|
65
|
+
id: membership.id,
|
|
66
|
+
displayName: membership.displayName,
|
|
67
|
+
slug: membership.slug,
|
|
68
|
+
domain: membership.domain,
|
|
69
|
+
isActive: membership.isActive,
|
|
70
|
+
roles: membership.roles,
|
|
71
|
+
homeRegion: membership.homeRegion,
|
|
72
|
+
hqCountryCode: membership.hqCountryCode,
|
|
73
|
+
directMembership: true,
|
|
74
|
+
children: [],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function tenantHierarchyItemFromRecord(record) {
|
|
78
|
+
const id = optionalString(record.id) ?? optionalString(record._id);
|
|
79
|
+
if (!id)
|
|
80
|
+
return null;
|
|
81
|
+
const slug = optionalString(record.slug) ?? id;
|
|
82
|
+
const displayName = optionalString(record.displayName) ??
|
|
83
|
+
optionalString(record.name) ??
|
|
84
|
+
optionalString(record.title) ??
|
|
85
|
+
slug;
|
|
86
|
+
return {
|
|
87
|
+
id,
|
|
88
|
+
displayName,
|
|
89
|
+
slug,
|
|
90
|
+
domain: optionalString(record.domain),
|
|
91
|
+
isActive: record.isActive !== false,
|
|
92
|
+
roles: [],
|
|
93
|
+
homeRegion: typeof record.homeRegion === "string" || record.homeRegion === null
|
|
94
|
+
? record.homeRegion
|
|
95
|
+
: undefined,
|
|
96
|
+
hqCountryCode: typeof record.hqCountryCode === "string" || record.hqCountryCode === null
|
|
97
|
+
? record.hqCountryCode
|
|
98
|
+
: undefined,
|
|
99
|
+
parentId: tenantParentIdFromRecord(record),
|
|
100
|
+
directMembership: false,
|
|
101
|
+
children: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function mergeTenantHierarchyItem(existing, next) {
|
|
105
|
+
if (!existing)
|
|
106
|
+
return next;
|
|
107
|
+
return {
|
|
108
|
+
...existing,
|
|
109
|
+
...next,
|
|
110
|
+
displayName: next.displayName || existing.displayName,
|
|
111
|
+
slug: next.slug || existing.slug,
|
|
112
|
+
domain: next.domain ?? existing.domain,
|
|
113
|
+
roles: existing.roles.length ? existing.roles : next.roles,
|
|
114
|
+
directMembership: existing.directMembership || next.directMembership,
|
|
115
|
+
parentId: next.parentId ?? existing.parentId,
|
|
116
|
+
homeRegion: next.homeRegion ?? existing.homeRegion,
|
|
117
|
+
hqCountryCode: next.hqCountryCode ?? existing.hqCountryCode,
|
|
118
|
+
children: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function compareTenantHierarchyItems(left, right) {
|
|
122
|
+
return left.displayName.localeCompare(right.displayName, undefined, {
|
|
123
|
+
sensitivity: "base",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
export function buildTenantHierarchy(directMemberships, childRecords = []) {
|
|
127
|
+
const byId = new Map();
|
|
128
|
+
for (const membership of directMemberships) {
|
|
129
|
+
byId.set(membership.id, mergeTenantHierarchyItem(byId.get(membership.id), tenantHierarchyItemFromMembership(membership)));
|
|
130
|
+
}
|
|
131
|
+
for (const record of childRecords) {
|
|
132
|
+
if (!isRecord(record))
|
|
133
|
+
continue;
|
|
134
|
+
const item = tenantHierarchyItemFromRecord(record);
|
|
135
|
+
if (!item)
|
|
136
|
+
continue;
|
|
137
|
+
byId.set(item.id, mergeTenantHierarchyItem(byId.get(item.id), item));
|
|
138
|
+
}
|
|
139
|
+
for (const item of byId.values()) {
|
|
140
|
+
item.children = [];
|
|
141
|
+
}
|
|
142
|
+
const roots = [];
|
|
143
|
+
for (const item of byId.values()) {
|
|
144
|
+
if (item.parentId && item.parentId !== item.id && byId.has(item.parentId)) {
|
|
145
|
+
byId.get(item.parentId).children.push(item);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
roots.push(item);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const sortTree = (items) => {
|
|
152
|
+
items.sort(compareTenantHierarchyItems);
|
|
153
|
+
for (const item of items) {
|
|
154
|
+
sortTree(item.children);
|
|
155
|
+
}
|
|
156
|
+
return items;
|
|
157
|
+
};
|
|
158
|
+
return sortTree(roots);
|
|
159
|
+
}
|
|
160
|
+
function flattenTenantHierarchy(roots) {
|
|
161
|
+
const items = [];
|
|
162
|
+
const visit = (item) => {
|
|
163
|
+
items.push(item);
|
|
164
|
+
for (const child of item.children)
|
|
165
|
+
visit(child);
|
|
166
|
+
};
|
|
167
|
+
for (const root of roots)
|
|
168
|
+
visit(root);
|
|
169
|
+
return items;
|
|
170
|
+
}
|
|
171
|
+
function tenantHierarchyStatus(item, activeTenantId) {
|
|
172
|
+
const labels = [];
|
|
173
|
+
if (item.roles.length)
|
|
174
|
+
labels.push(item.roles.join(", "));
|
|
175
|
+
if (!item.directMembership)
|
|
176
|
+
labels.push("visible via parent");
|
|
177
|
+
if (activeTenantId === item.id)
|
|
178
|
+
labels.push("active");
|
|
179
|
+
return labels.length ? ` [${labels.join("; ")}]` : "";
|
|
180
|
+
}
|
|
181
|
+
function tenantHierarchyLineText(item, prefix, isLast, isRoot, activeTenantId) {
|
|
182
|
+
const connector = isRoot ? "" : isLast ? "`- " : "|- ";
|
|
183
|
+
const domain = item.domain ? ` (${item.domain})` : "";
|
|
184
|
+
return `${prefix}${connector}${item.slug} - ${item.displayName}${domain}${tenantHierarchyStatus(item, activeTenantId)}`;
|
|
185
|
+
}
|
|
186
|
+
export function buildTenantHierarchyTreeLines(roots, options) {
|
|
187
|
+
const lines = [];
|
|
188
|
+
const visit = (item, prefix, isLast, isRoot) => {
|
|
189
|
+
lines.push(tenantHierarchyLineText(item, prefix, isLast, isRoot, options?.activeTenantId));
|
|
190
|
+
const childPrefix = isRoot ? "" : `${prefix}${isLast ? " " : "| "}`;
|
|
191
|
+
item.children.forEach((child, index) => visit(child, childPrefix, index === item.children.length - 1, false));
|
|
192
|
+
};
|
|
193
|
+
roots.forEach((root, index) => visit(root, "", index === roots.length - 1, true));
|
|
194
|
+
return lines;
|
|
195
|
+
}
|
|
196
|
+
function tenantHierarchyJson(item) {
|
|
197
|
+
return {
|
|
198
|
+
id: item.id,
|
|
199
|
+
displayName: item.displayName,
|
|
200
|
+
slug: item.slug,
|
|
201
|
+
domain: item.domain,
|
|
202
|
+
isActive: item.isActive,
|
|
203
|
+
roles: item.roles,
|
|
204
|
+
homeRegion: item.homeRegion,
|
|
205
|
+
hqCountryCode: item.hqCountryCode,
|
|
206
|
+
parentId: item.parentId,
|
|
207
|
+
directMembership: item.directMembership,
|
|
208
|
+
children: item.children.map(tenantHierarchyJson),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async function loadTenantHierarchy(options) {
|
|
212
|
+
const warnings = [];
|
|
213
|
+
const childRecords = [];
|
|
214
|
+
const parentMembership = options.parentId
|
|
215
|
+
? options.memberships.find((membership) => membership.id === options.parentId || membership.slug === options.parentId)
|
|
216
|
+
: undefined;
|
|
217
|
+
const rootMemberships = options.parentId
|
|
218
|
+
? [
|
|
219
|
+
parentMembership ??
|
|
220
|
+
{
|
|
221
|
+
id: options.parentId,
|
|
222
|
+
displayName: options.parentId,
|
|
223
|
+
slug: options.parentId,
|
|
224
|
+
isActive: true,
|
|
225
|
+
roles: [],
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
: options.memberships;
|
|
229
|
+
const parentsToQuery = options.parentId
|
|
230
|
+
? [options.parentId]
|
|
231
|
+
: options.memberships.map((membership) => membership.id);
|
|
232
|
+
for (const parentId of parentsToQuery) {
|
|
233
|
+
const client = new PlatformAPIClient(options.publicApiUrl, parentId);
|
|
234
|
+
try {
|
|
235
|
+
const response = await client.listTenantChildren(parentId, {
|
|
236
|
+
includeDescendants: true,
|
|
237
|
+
limit: 100,
|
|
238
|
+
});
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
const body = await response.text().catch(() => "");
|
|
241
|
+
const message = `Could not load child tenants for ${parentId}: ${response.status} ${response.statusText}`.trim();
|
|
242
|
+
warnings.push(message);
|
|
243
|
+
options.debug?.(message, body || undefined);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
childRecords.push(...tenantRecordsFromPayload(await response.json()));
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
const message = error instanceof Error
|
|
250
|
+
? `Could not load child tenants for ${parentId}: ${error.message}`
|
|
251
|
+
: `Could not load child tenants for ${parentId}`;
|
|
252
|
+
warnings.push(message);
|
|
253
|
+
options.debug?.(message);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
roots: buildTenantHierarchy(rootMemberships, childRecords),
|
|
258
|
+
warnings,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async function promptForTenantFromHierarchy(roots) {
|
|
262
|
+
const choices = [];
|
|
263
|
+
const visit = (item, prefix, isLast, isRoot) => {
|
|
264
|
+
choices.push({
|
|
265
|
+
name: tenantHierarchyLineText(item, prefix, isLast, isRoot),
|
|
266
|
+
value: item.id,
|
|
267
|
+
disabled: item.directMembership
|
|
268
|
+
? undefined
|
|
269
|
+
: "Visible through parent hierarchy; direct tenant-admin membership is required to select.",
|
|
270
|
+
});
|
|
271
|
+
const childPrefix = isRoot ? "" : `${prefix}${isLast ? " " : "| "}`;
|
|
272
|
+
item.children.forEach((child, index) => visit(child, childPrefix, index === item.children.length - 1, false));
|
|
273
|
+
};
|
|
274
|
+
roots.forEach((root, index) => visit(root, "", index === roots.length - 1, true));
|
|
275
|
+
const { tenantId } = await inquirer.prompt([
|
|
276
|
+
{
|
|
277
|
+
type: "select",
|
|
278
|
+
name: "tenantId",
|
|
279
|
+
message: "Select the tenant to work with now",
|
|
280
|
+
choices,
|
|
281
|
+
},
|
|
282
|
+
]);
|
|
283
|
+
return String(tenantId);
|
|
284
|
+
}
|
|
285
|
+
export function extractCreatedTenantRecord(payload) {
|
|
286
|
+
const nestedDoc = payload.doc;
|
|
287
|
+
if (nestedDoc && typeof nestedDoc === "object" && !Array.isArray(nestedDoc)) {
|
|
288
|
+
return nestedDoc;
|
|
289
|
+
}
|
|
290
|
+
return payload;
|
|
291
|
+
}
|
|
292
|
+
function normalizeTenantCreateHomeRegion(value) {
|
|
293
|
+
if (value === undefined || value === null || value === "")
|
|
294
|
+
return undefined;
|
|
295
|
+
const region = normalizeHomeRegion(String(value));
|
|
296
|
+
if (!region) {
|
|
297
|
+
throw new Error("home-region must be one of au, ca, or eu.");
|
|
298
|
+
}
|
|
299
|
+
return region;
|
|
300
|
+
}
|
|
301
|
+
async function resolveChildTenantHomeRegion(options) {
|
|
302
|
+
const requested = normalizeTenantCreateHomeRegion(options.requested);
|
|
303
|
+
if (requested)
|
|
304
|
+
return requested;
|
|
305
|
+
const parentRegion = normalizeHomeRegion(options.parentHomeRegion);
|
|
306
|
+
if (!options.interactive)
|
|
307
|
+
return parentRegion || undefined;
|
|
308
|
+
const answer = await inquirer.prompt([
|
|
309
|
+
{
|
|
310
|
+
type: "select",
|
|
311
|
+
name: "homeRegion",
|
|
312
|
+
message: "Child tenant home region:",
|
|
313
|
+
default: parentRegion || undefined,
|
|
314
|
+
choices: HOME_REGION_CHOICES,
|
|
315
|
+
},
|
|
316
|
+
]);
|
|
317
|
+
return normalizeTenantCreateHomeRegion(answer.homeRegion);
|
|
318
|
+
}
|
|
319
|
+
export function buildTenantListZeroState(tokens) {
|
|
320
|
+
const zeroState = {
|
|
321
|
+
headline: "No active tenant-admin memberships found for the current login.",
|
|
322
|
+
hint: "Use `eai whoami` to inspect the authenticated tenant context.",
|
|
323
|
+
};
|
|
324
|
+
if (tokens.tenantName || tokens.tenantId) {
|
|
325
|
+
const tenantName = tokens.tenantName || "current authenticated tenant";
|
|
326
|
+
const tenantId = tokens.tenantId ? ` (${tokens.tenantId})` : "";
|
|
327
|
+
zeroState.tenantContext = `Authenticated tenant context: ${tenantName}${tenantId}`;
|
|
328
|
+
}
|
|
329
|
+
return zeroState;
|
|
330
|
+
}
|
|
331
|
+
export function buildTenantCreateStatusMessages(outcome) {
|
|
332
|
+
const messages = [];
|
|
333
|
+
if (outcome.bootstrap) {
|
|
334
|
+
if (outcome.bootstrap.status === "bootstrapped") {
|
|
335
|
+
messages.push("Bootstrap: first tenant admin was provisioned for the current login.");
|
|
336
|
+
}
|
|
337
|
+
else if (outcome.bootstrap.status === "already-usable") {
|
|
338
|
+
messages.push("Bootstrap: the current login already had direct tenant-admin on the child tenant.");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
else if (outcome.bootstrapError) {
|
|
342
|
+
const prefix = outcome.bootstrapError.code
|
|
343
|
+
? `${outcome.bootstrapError.code}: `
|
|
344
|
+
: "";
|
|
345
|
+
messages.push(`Bootstrap not confirmed: ${prefix}${outcome.bootstrapError.message}`);
|
|
346
|
+
}
|
|
347
|
+
if (outcome.usability.usable) {
|
|
348
|
+
messages.push(outcome.usability.autoSelected
|
|
349
|
+
? "Usable: direct tenant-admin confirmed and the new tenant was selected."
|
|
350
|
+
: "Usable: direct tenant-admin confirmed.");
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
messages.push("Usable: not yet confirmed. The tenant exists, but direct tenant-admin membership is not visible yet.");
|
|
354
|
+
}
|
|
355
|
+
return messages;
|
|
356
|
+
}
|
|
357
|
+
function reportPublicApiEnvSync(result) {
|
|
358
|
+
const notice = buildPublicApiEnvSyncNotice(result);
|
|
359
|
+
if (!notice)
|
|
360
|
+
return;
|
|
361
|
+
if (notice.level === "warn") {
|
|
362
|
+
out.warn(notice.message);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
out.success(notice.message);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
export function buildTenantBootstrapAdminStatusMessages(result) {
|
|
369
|
+
const messages = [];
|
|
370
|
+
if (result.status === "bootstrapped") {
|
|
371
|
+
messages.push("Bootstrap: tenant-admin access was provisioned for the target user.");
|
|
372
|
+
}
|
|
373
|
+
else if (result.status === "already-usable") {
|
|
374
|
+
messages.push("Bootstrap: the target user already had direct tenant-admin on the child tenant.");
|
|
375
|
+
}
|
|
376
|
+
messages.push(result.membershipCreated
|
|
377
|
+
? "Membership: child tenant membership was created."
|
|
378
|
+
: "Membership: child tenant membership already existed or did not need creation.");
|
|
379
|
+
messages.push(result.adminAssigned
|
|
380
|
+
? "Role: tenant-admin was assigned on the child tenant."
|
|
381
|
+
: "Role: tenant-admin was already assigned or did not need assignment.");
|
|
382
|
+
messages.push(result.usable
|
|
383
|
+
? "Usable: direct tenant-admin confirmed for the child tenant."
|
|
384
|
+
: "Usable: not yet confirmed. Re-run `eai tenant list` or `eai whoami` after membership propagation.");
|
|
385
|
+
return messages;
|
|
386
|
+
}
|
|
387
|
+
export const tenantCommand = new Command("tenant").description("Manage tenants on the platform");
|
|
388
|
+
const tenantStorageCommand = new Command("storage").description("Inspect tenant storage configuration");
|
|
389
|
+
tenantStorageCommand
|
|
390
|
+
.command("list")
|
|
391
|
+
.description("List published storage bindings and operational connections for the active tenant")
|
|
392
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
393
|
+
.action(async (options) => {
|
|
394
|
+
const root = await findProjectRoot();
|
|
395
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
396
|
+
const context = await resolveActiveTenantContext({
|
|
397
|
+
projectRoot: root || undefined,
|
|
398
|
+
publicApiUrl,
|
|
399
|
+
interactive: true,
|
|
400
|
+
});
|
|
401
|
+
const client = new PlatformAPIClient(publicApiUrl, context.activeTenant.id);
|
|
402
|
+
const response = await client.getStorageStatus();
|
|
403
|
+
if (!response.ok) {
|
|
404
|
+
out.error(`Failed to fetch storage status: ${response.status} ${response.statusText}`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
const payload = (await response.json());
|
|
408
|
+
if (options.format === "json") {
|
|
409
|
+
out.json(payload);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
out.success(`${payload.objectTypes.length} object type${payload.objectTypes.length === 1 ? "" : "s"} with storage metadata`);
|
|
413
|
+
for (const item of payload.objectTypes) {
|
|
414
|
+
const readiness = item.isReady
|
|
415
|
+
? chalk.green("ready")
|
|
416
|
+
: chalk.yellow("pending");
|
|
417
|
+
out.info(`${chalk.cyan(item.objectType)} [${item.backend}] ${readiness} ${chalk.dim(`(${item.routeSource})`)}`);
|
|
418
|
+
}
|
|
419
|
+
if (payload.connections.length > 0) {
|
|
420
|
+
out.blank();
|
|
421
|
+
out.info(chalk.bold("Operational connections"));
|
|
422
|
+
for (const connection of payload.connections) {
|
|
423
|
+
const target = connection.index_name ||
|
|
424
|
+
connection.container_name ||
|
|
425
|
+
connection.database_name ||
|
|
426
|
+
connection.endpoint ||
|
|
427
|
+
"configured";
|
|
428
|
+
out.info(`${chalk.cyan(connection.storage_backend)} — ${chalk.dim(target)}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
tenantStorageCommand
|
|
433
|
+
.command("verify")
|
|
434
|
+
.description("Check tenant storage readiness across published Object Types")
|
|
435
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
436
|
+
.action(async (options) => {
|
|
437
|
+
const root = await findProjectRoot();
|
|
438
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
439
|
+
const context = await resolveActiveTenantContext({
|
|
440
|
+
projectRoot: root || undefined,
|
|
441
|
+
publicApiUrl,
|
|
442
|
+
interactive: true,
|
|
443
|
+
});
|
|
444
|
+
const client = new PlatformAPIClient(publicApiUrl, context.activeTenant.id);
|
|
445
|
+
const response = await client.getStorageDoctor();
|
|
446
|
+
if (!response.ok) {
|
|
447
|
+
out.error(`Storage verification failed: ${response.status} ${response.statusText}`);
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
const payload = (await response.json());
|
|
451
|
+
if (options.format === "json") {
|
|
452
|
+
out.json(payload);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
out[payload.healthy ? "success" : "warn"](payload.healthy
|
|
456
|
+
? "Tenant storage is healthy."
|
|
457
|
+
: "Tenant storage needs attention.");
|
|
458
|
+
for (const check of payload.checks) {
|
|
459
|
+
const status = check.healthy
|
|
460
|
+
? chalk.green("healthy")
|
|
461
|
+
: chalk.yellow("needs-attention");
|
|
462
|
+
const issues = check.issues?.length
|
|
463
|
+
? chalk.dim(` — ${check.issues.join("; ")}`)
|
|
464
|
+
: "";
|
|
465
|
+
out.info(`${chalk.cyan(check.objectType)} [${check.backend}] ${status}${issues}`);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
tenantCommand.addCommand(tenantStorageCommand);
|
|
469
|
+
// ─── eai tenant list ──────────────────────────────────────────────────────
|
|
470
|
+
tenantCommand
|
|
471
|
+
.command("list")
|
|
472
|
+
.description("List tenants where the current user is a tenant-admin (default) or all roles with --all")
|
|
473
|
+
.option("--parent <id>", "Parent tenant ID")
|
|
474
|
+
.option("--all", "Include tenants where the user holds non-admin roles (e.g. tenant-viewer)", false)
|
|
475
|
+
.option("--debug", "Show debug diagnostics for tenant lookup", false)
|
|
476
|
+
.option("--raw-user", "Print raw membership payload in debug mode", false)
|
|
477
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
478
|
+
.option("--json", "Output raw JSON (deprecated, use --format json)", false)
|
|
479
|
+
.addHelpText("after", `
|
|
480
|
+
Examples:
|
|
481
|
+
$ eai tenant list
|
|
482
|
+
$ eai tenant list --parent <tenant-id> # show the child hierarchy for a parent
|
|
483
|
+
$ eai tenant list --all # include tenant-viewer / tenant-builder memberships
|
|
484
|
+
$ eai tenant list --debug
|
|
485
|
+
$ eai tenant list --debug --raw-user
|
|
486
|
+
$ eai tenant list --format json | jq '.tenants[] | .name'
|
|
487
|
+
`)
|
|
488
|
+
.action(async (options) => {
|
|
489
|
+
if (options.json)
|
|
490
|
+
options.format = "json";
|
|
491
|
+
const debugEnabled = Boolean(options.debug);
|
|
492
|
+
const debug = (message, data) => {
|
|
493
|
+
if (!debugEnabled)
|
|
494
|
+
return;
|
|
495
|
+
if (data === undefined) {
|
|
496
|
+
console.error(`[debug] ${message}`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const value = out.redactSensitiveText(typeof data === "string" ? data : JSON.stringify(data, null, 2));
|
|
500
|
+
console.error(`[debug] ${message}: ${value}`);
|
|
501
|
+
};
|
|
502
|
+
const tokens = await loadTokens();
|
|
503
|
+
if (!tokens?.oid) {
|
|
504
|
+
exitWithError(ErrorCode.E101);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
debug("Authenticated token loaded", {
|
|
508
|
+
oid: tokens.oid ? "[present]" : "[missing]",
|
|
509
|
+
upn: tokens.upn ? "[present]" : "[missing]",
|
|
510
|
+
expiresAt: new Date(tokens.expiresAt).toISOString(),
|
|
511
|
+
});
|
|
512
|
+
const root = await findProjectRoot();
|
|
513
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
514
|
+
debug("Project root", root || "(none)");
|
|
515
|
+
debug("Using Public API URL", publicApiUrl);
|
|
516
|
+
const spinner = options.format === "json" ? null : ora("Fetching tenants...").start();
|
|
517
|
+
try {
|
|
518
|
+
const membershipsResponse = await fetchTenantAdminMemberships(publicApiUrl);
|
|
519
|
+
debug("Membership lookup status", "ok");
|
|
520
|
+
if (debugEnabled && options.rawUser) {
|
|
521
|
+
debug("Raw membership payload", membershipsResponse);
|
|
522
|
+
}
|
|
523
|
+
const tenants = options.all
|
|
524
|
+
? membershipsResponse.memberships.filter((membership) => membership.isActive !== false)
|
|
525
|
+
: membershipsResponse.memberships;
|
|
526
|
+
debug(options.all
|
|
527
|
+
? "Tenant entries (all roles, active only)"
|
|
528
|
+
: "Tenant entries after tenant-admin filtering", tenants.length);
|
|
529
|
+
const hierarchy = await loadTenantHierarchy({
|
|
530
|
+
publicApiUrl: membershipsResponse.publicApiUrl,
|
|
531
|
+
memberships: tenants,
|
|
532
|
+
parentId: options.parent,
|
|
533
|
+
debug,
|
|
534
|
+
});
|
|
535
|
+
const visible = flattenTenantHierarchy(hierarchy.roots);
|
|
536
|
+
const selectable = visible.filter((tenant) => tenant.directMembership);
|
|
537
|
+
debug("Tenant hierarchy entries after filtering", visible.length);
|
|
538
|
+
if (options.format === "json") {
|
|
539
|
+
out.json({
|
|
540
|
+
tenants: visible.map((tenant) => ({
|
|
541
|
+
id: tenant.id,
|
|
542
|
+
displayName: tenant.displayName,
|
|
543
|
+
slug: tenant.slug,
|
|
544
|
+
domain: tenant.domain,
|
|
545
|
+
isActive: tenant.isActive,
|
|
546
|
+
roles: tenant.roles,
|
|
547
|
+
homeRegion: tenant.homeRegion,
|
|
548
|
+
hqCountryCode: tenant.hqCountryCode,
|
|
549
|
+
parentId: tenant.parentId,
|
|
550
|
+
directMembership: tenant.directMembership,
|
|
551
|
+
active: tokens.activeTenantId === tenant.id,
|
|
552
|
+
})),
|
|
553
|
+
hierarchy: hierarchy.roots.map(tenantHierarchyJson),
|
|
554
|
+
count: visible.length,
|
|
555
|
+
selectableCount: selectable.length,
|
|
556
|
+
});
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const countLabel = visible.length === selectable.length
|
|
560
|
+
? `${visible.length} tenant-admin membership${visible.length !== 1 ? "s" : ""}`
|
|
561
|
+
: `${visible.length} visible tenant${visible.length !== 1 ? "s" : ""} (${selectable.length} selectable tenant-admin membership${selectable.length !== 1 ? "s" : ""})`;
|
|
562
|
+
spinner.succeed(countLabel);
|
|
563
|
+
for (const warning of hierarchy.warnings) {
|
|
564
|
+
out.warn(warning);
|
|
565
|
+
}
|
|
566
|
+
if (visible.length === 0) {
|
|
567
|
+
const zeroState = buildTenantListZeroState(tokens);
|
|
568
|
+
out.info(zeroState.headline);
|
|
569
|
+
if (zeroState.tenantContext) {
|
|
570
|
+
out.info(`Authenticated tenant context: ${chalk.cyan(tokens.tenantName || "current authenticated tenant")}${tokens.tenantId ? chalk.dim(` (${tokens.tenantId})`) : ""}`);
|
|
571
|
+
}
|
|
572
|
+
out.info(`Use ${chalk.cyan("eai whoami")} to inspect the authenticated tenant context.`);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
for (const line of buildTenantHierarchyTreeLines(hierarchy.roots, {
|
|
576
|
+
activeTenantId: tokens.activeTenantId,
|
|
577
|
+
})) {
|
|
578
|
+
out.info(line);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
catch (err) {
|
|
582
|
+
if (spinner)
|
|
583
|
+
spinner.fail(err instanceof Error ? err.message : String(err));
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
// ─── eai tenant select ───────────────────────────────────────────────────
|
|
588
|
+
tenantCommand
|
|
589
|
+
.command("select [tenant]")
|
|
590
|
+
.description("Select the active tenant to work with")
|
|
591
|
+
.action(async (tenant) => {
|
|
592
|
+
const root = await findProjectRoot();
|
|
593
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
594
|
+
try {
|
|
595
|
+
let tenantId = tenant;
|
|
596
|
+
if (!tenantId) {
|
|
597
|
+
const fetched = await fetchTenantAdminMemberships(publicApiUrl);
|
|
598
|
+
const hierarchy = await loadTenantHierarchy({
|
|
599
|
+
publicApiUrl: fetched.publicApiUrl,
|
|
600
|
+
memberships: fetched.memberships,
|
|
601
|
+
});
|
|
602
|
+
for (const warning of hierarchy.warnings) {
|
|
603
|
+
out.warn(warning);
|
|
604
|
+
}
|
|
605
|
+
const selectable = flattenTenantHierarchy(hierarchy.roots).filter((item) => item.directMembership);
|
|
606
|
+
if (selectable.length === 0) {
|
|
607
|
+
throw new Error("No active tenant-admin memberships found for the current login. Run `eai tenant list` to inspect your access.");
|
|
608
|
+
}
|
|
609
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
610
|
+
if (selectable.length !== 1) {
|
|
611
|
+
throw new Error("Multiple active tenant-admin memberships found. Run `eai tenant select <tenant>` to choose one.");
|
|
612
|
+
}
|
|
613
|
+
tenantId = selectable[0].id;
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
tenantId = await promptForTenantFromHierarchy(hierarchy.roots);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
const context = await resolveActiveTenantContext({
|
|
620
|
+
projectRoot: root || undefined,
|
|
621
|
+
publicApiUrl,
|
|
622
|
+
interactive: false,
|
|
623
|
+
forcePrompt: false,
|
|
624
|
+
tenantId,
|
|
625
|
+
});
|
|
626
|
+
out.success(`Active tenant set to ${chalk.cyan(context.activeTenant.slug)} (${chalk.dim(context.activeTenant.id)})`);
|
|
627
|
+
reportPublicApiEnvSync(context.publicApiEnvSync);
|
|
628
|
+
}
|
|
629
|
+
catch (err) {
|
|
630
|
+
out.error(err instanceof Error ? err.message : String(err));
|
|
631
|
+
process.exit(1);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
// ─── eai tenant info <id> ─────────────────────────────────────────────────
|
|
635
|
+
tenantCommand
|
|
636
|
+
.command("info <id>")
|
|
637
|
+
.description("Show tenant details")
|
|
638
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
639
|
+
.option("--json", "Output raw JSON (deprecated, use --format json)", false)
|
|
640
|
+
.action(async (id, options) => {
|
|
641
|
+
if (options.json)
|
|
642
|
+
options.format = "json";
|
|
643
|
+
const root = await findProjectRoot();
|
|
644
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
645
|
+
const spinner = options.format === "json" ? null : ora("Fetching tenant...").start();
|
|
646
|
+
try {
|
|
647
|
+
const memberships = await fetchTenantAdminMemberships(publicApiUrl);
|
|
648
|
+
const tenant = memberships.memberships.find((entry) => entry.id === id || entry.slug === id);
|
|
649
|
+
if (!tenant) {
|
|
650
|
+
if (spinner)
|
|
651
|
+
spinner.fail("404 Not Found");
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
if (options.format === "json") {
|
|
655
|
+
out.json(tenant);
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
spinner.succeed(`Tenant: ${chalk.cyan(tenant.displayName)}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
catch (err) {
|
|
662
|
+
if (spinner)
|
|
663
|
+
spinner.fail(err instanceof Error ? err.message : String(err));
|
|
664
|
+
process.exit(1);
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
// ─── eai tenant create ───────────────────────────────────────────────────
|
|
668
|
+
tenantCommand
|
|
669
|
+
.command("create")
|
|
670
|
+
.description("Create a new tenant")
|
|
671
|
+
.requiredOption("--name <name>", "Tenant name")
|
|
672
|
+
.requiredOption("--slug <slug>", "Tenant slug (kebab-case)")
|
|
673
|
+
.option("--parent <id>", "Parent tenant ID")
|
|
674
|
+
.option("--domain <domains>", "Comma-separated domain list")
|
|
675
|
+
.option("--usecase <usecase>", "Tenant usecase: council|retail|healthcare|finance|manufacturing|generic", "generic")
|
|
676
|
+
.option("--industry <industry>", "Signup/onboarding industry segment")
|
|
677
|
+
.option("--starter-template <key>", "Starter application template key", "eai-app-template")
|
|
678
|
+
.option("--home-region <region>", "Tenant home region: au|ca|eu")
|
|
679
|
+
.option("--allow-root", "Allow root tenant creation for administrative backfills", false)
|
|
680
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
681
|
+
.option("--json", "Output raw JSON (deprecated, use --format json)", false)
|
|
682
|
+
.action(async (options) => {
|
|
683
|
+
if (options.json)
|
|
684
|
+
options.format = "json";
|
|
685
|
+
if (!options.parent && !options.allowRoot) {
|
|
686
|
+
out.error("Root tenant creation is guarded. Complete onboarding for the main company tenant, then use `eai init --parent-tenant <id>` or pass --parent for child tenants.");
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
let rootHomeRegion;
|
|
690
|
+
if (!options.parent) {
|
|
691
|
+
try {
|
|
692
|
+
rootHomeRegion = normalizeTenantCreateHomeRegion(options.homeRegion);
|
|
693
|
+
}
|
|
694
|
+
catch (err) {
|
|
695
|
+
out.error(err instanceof Error ? err.message : String(err));
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
if (!rootHomeRegion) {
|
|
699
|
+
out.error("--home-region au|ca|eu is required with --allow-root because root tenants cannot inherit a parent region.");
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const root = await findProjectRoot();
|
|
704
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
705
|
+
let spinner = null;
|
|
706
|
+
try {
|
|
707
|
+
const context = await resolveActiveTenantContext({
|
|
708
|
+
projectRoot: root || undefined,
|
|
709
|
+
publicApiUrl,
|
|
710
|
+
interactive: true,
|
|
711
|
+
tenantId: options.parent || undefined,
|
|
712
|
+
});
|
|
713
|
+
const client = new PlatformAPIClient(publicApiUrl, context.activeTenant.id);
|
|
714
|
+
const tenantHomeRegion = options.parent
|
|
715
|
+
? await resolveChildTenantHomeRegion({
|
|
716
|
+
requested: options.homeRegion,
|
|
717
|
+
parentHomeRegion: context.activeTenant.homeRegion,
|
|
718
|
+
interactive: options.format !== "json" &&
|
|
719
|
+
Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
720
|
+
})
|
|
721
|
+
: rootHomeRegion;
|
|
722
|
+
spinner =
|
|
723
|
+
options.format === "json"
|
|
724
|
+
? null
|
|
725
|
+
: ora(`Creating tenant "${options.name}"...`).start();
|
|
726
|
+
const res = await client.createTenant({
|
|
727
|
+
name: options.name,
|
|
728
|
+
slug: options.slug,
|
|
729
|
+
parent: options.parent,
|
|
730
|
+
domain: options.domain?.split(",").map((d) => d.trim()),
|
|
731
|
+
usecase: options.usecase,
|
|
732
|
+
industry: options.industry,
|
|
733
|
+
starterTemplate: options.starterTemplate,
|
|
734
|
+
homeRegion: tenantHomeRegion,
|
|
735
|
+
});
|
|
736
|
+
if (!res.ok) {
|
|
737
|
+
const body = await res.text();
|
|
738
|
+
if (spinner) {
|
|
739
|
+
spinner.fail(`${res.status}: ${body}`);
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
process.stderr.write(`${body || res.statusText}\n`);
|
|
743
|
+
}
|
|
744
|
+
process.exit(1);
|
|
745
|
+
}
|
|
746
|
+
const tenant = (await res.json());
|
|
747
|
+
const createdTenant = extractCreatedTenantRecord(tenant);
|
|
748
|
+
const tenantId = String(createdTenant.id || "");
|
|
749
|
+
let bootstrap;
|
|
750
|
+
let bootstrapError;
|
|
751
|
+
let bootstrapped = false;
|
|
752
|
+
const refreshStatus = async (bootstrappedFlag) => {
|
|
753
|
+
if (!tenantId) {
|
|
754
|
+
return {
|
|
755
|
+
status: {
|
|
756
|
+
tenantId,
|
|
757
|
+
created: true,
|
|
758
|
+
bootstrapped: bootstrappedFlag,
|
|
759
|
+
membershipConfirmed: false,
|
|
760
|
+
adminConfirmed: false,
|
|
761
|
+
usable: false,
|
|
762
|
+
autoSelected: false,
|
|
763
|
+
},
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
try {
|
|
767
|
+
return await refreshTenantUsabilityStatus(tenantId, {
|
|
768
|
+
publicApiUrl,
|
|
769
|
+
created: true,
|
|
770
|
+
bootstrapped: bootstrappedFlag,
|
|
771
|
+
autoSelect: Boolean(options.parent),
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
catch (error) {
|
|
775
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
776
|
+
bootstrapError ??= {
|
|
777
|
+
status: 0,
|
|
778
|
+
code: "MEMBERSHIP_REFRESH_FAILED",
|
|
779
|
+
message,
|
|
780
|
+
};
|
|
781
|
+
return {
|
|
782
|
+
status: {
|
|
783
|
+
tenantId,
|
|
784
|
+
created: true,
|
|
785
|
+
bootstrapped: bootstrappedFlag,
|
|
786
|
+
membershipConfirmed: false,
|
|
787
|
+
adminConfirmed: false,
|
|
788
|
+
usable: false,
|
|
789
|
+
autoSelected: false,
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
let refreshed = await refreshStatus(bootstrapped);
|
|
795
|
+
if (options.parent && tenantId && !refreshed.status.usable) {
|
|
796
|
+
const tokens = await loadTokens();
|
|
797
|
+
if (tokens?.oid) {
|
|
798
|
+
const bootstrapResponse = await client.bootstrapChildTenantAdmin(options.parent, tenantId, {
|
|
799
|
+
userOid: tokens.oid,
|
|
800
|
+
userEmail: tokens.upn,
|
|
801
|
+
});
|
|
802
|
+
if (bootstrapResponse.ok) {
|
|
803
|
+
bootstrap =
|
|
804
|
+
(await bootstrapResponse.json());
|
|
805
|
+
bootstrapped =
|
|
806
|
+
bootstrap.status === "bootstrapped" ||
|
|
807
|
+
bootstrap.status === "already-usable";
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
bootstrapError = await parseApiError(bootstrapResponse);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
bootstrapError = {
|
|
815
|
+
status: 0,
|
|
816
|
+
code: "OID_MISSING",
|
|
817
|
+
message: "The current login is missing an oid claim, so child bootstrap was not attempted.",
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
refreshed = await refreshStatus(bootstrapped);
|
|
821
|
+
}
|
|
822
|
+
const outcome = {
|
|
823
|
+
tenant,
|
|
824
|
+
bootstrap,
|
|
825
|
+
bootstrapError,
|
|
826
|
+
usability: refreshed.status,
|
|
827
|
+
};
|
|
828
|
+
if (options.format === "json") {
|
|
829
|
+
out.json({
|
|
830
|
+
tenant,
|
|
831
|
+
bootstrap: bootstrap || null,
|
|
832
|
+
bootstrapError: bootstrapError || null,
|
|
833
|
+
usability: outcome.usability,
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
spinner.succeed(`Created tenant ${chalk.cyan(String(createdTenant.slug || options.slug))} (${chalk.dim(String(createdTenant.id || tenantId))})`);
|
|
838
|
+
for (const message of buildTenantCreateStatusMessages(outcome)) {
|
|
839
|
+
if (message.startsWith("Usable: not yet confirmed") ||
|
|
840
|
+
message.startsWith("Bootstrap not confirmed")) {
|
|
841
|
+
out.warn(message);
|
|
842
|
+
}
|
|
843
|
+
else if (message.startsWith("Usable:")) {
|
|
844
|
+
out.success(message);
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
out.info(message);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
catch (err) {
|
|
853
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
854
|
+
if (spinner) {
|
|
855
|
+
spinner.fail(message);
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
out.error(message);
|
|
859
|
+
}
|
|
860
|
+
process.exit(1);
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
// ─── eai tenant bootstrap-admin ──────────────────────────────────────────
|
|
864
|
+
tenantCommand
|
|
865
|
+
.command("bootstrap-admin")
|
|
866
|
+
.description("Bootstrap first tenant-admin access for an existing child tenant")
|
|
867
|
+
.requiredOption("--parent <id>", "Direct parent tenant ID")
|
|
868
|
+
.requiredOption("--child <id>", "Immediate child tenant ID")
|
|
869
|
+
.option("--user-oid <oid>", "Target user object ID (defaults to the current login)")
|
|
870
|
+
.option("--user-email <email>", "Target user email (defaults to the current login email when available)")
|
|
871
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
872
|
+
.option("--json", "Output raw JSON (deprecated, use --format json)", false)
|
|
873
|
+
.addHelpText("after", `
|
|
874
|
+
Examples:
|
|
875
|
+
$ eai tenant bootstrap-admin --parent <parent-tenant-id> --child <child-tenant-id>
|
|
876
|
+
$ eai tenant bootstrap-admin --parent <parent-tenant-id> --child <child-tenant-id> --user-oid <entra-user-oid> --user-email user@example.com
|
|
877
|
+
`)
|
|
878
|
+
.action(async (options) => {
|
|
879
|
+
if (options.json)
|
|
880
|
+
options.format = "json";
|
|
881
|
+
if (!["text", "json"].includes(options.format)) {
|
|
882
|
+
out.error("Unsupported format. Use text or json.");
|
|
883
|
+
process.exit(1);
|
|
884
|
+
}
|
|
885
|
+
const root = await findProjectRoot();
|
|
886
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
887
|
+
const tokens = await loadTokens();
|
|
888
|
+
const userOid = options.userOid || tokens?.oid;
|
|
889
|
+
const userEmail = options.userEmail || tokens?.upn;
|
|
890
|
+
if (!userOid) {
|
|
891
|
+
const message = "The current login is missing an oid claim. Pass --user-oid <entra-user-oid> or run `eai login` again.";
|
|
892
|
+
if (options.format === "json") {
|
|
893
|
+
out.json({
|
|
894
|
+
parentTenantId: options.parent,
|
|
895
|
+
childTenantId: options.child,
|
|
896
|
+
bootstrapped: false,
|
|
897
|
+
error: {
|
|
898
|
+
code: "OID_MISSING",
|
|
899
|
+
message,
|
|
900
|
+
},
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
out.error(message);
|
|
905
|
+
}
|
|
906
|
+
process.exit(1);
|
|
907
|
+
}
|
|
908
|
+
const spinner = options.format === "json"
|
|
909
|
+
? null
|
|
910
|
+
: ora(`Bootstrapping tenant-admin for ${userEmail || userOid} on child tenant ${options.child}...`).start();
|
|
911
|
+
try {
|
|
912
|
+
await resolveActiveTenantContext({
|
|
913
|
+
projectRoot: root || undefined,
|
|
914
|
+
publicApiUrl,
|
|
915
|
+
interactive: true,
|
|
916
|
+
tenantId: options.parent,
|
|
917
|
+
});
|
|
918
|
+
const client = new PlatformAPIClient(publicApiUrl, options.parent);
|
|
919
|
+
const response = await client.bootstrapChildTenantAdmin(options.parent, options.child, {
|
|
920
|
+
userOid,
|
|
921
|
+
userEmail,
|
|
922
|
+
});
|
|
923
|
+
if (!response.ok) {
|
|
924
|
+
const error = await parseApiError(response);
|
|
925
|
+
if (options.format === "json") {
|
|
926
|
+
out.json({
|
|
927
|
+
parentTenantId: options.parent,
|
|
928
|
+
childTenantId: options.child,
|
|
929
|
+
userOid,
|
|
930
|
+
userEmail,
|
|
931
|
+
bootstrapped: false,
|
|
932
|
+
error,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
else if (spinner) {
|
|
936
|
+
const prefix = error.code ? `${error.code}: ` : "";
|
|
937
|
+
spinner.fail(`${error.status}: ${prefix}${error.message}`);
|
|
938
|
+
}
|
|
939
|
+
process.exit(1);
|
|
940
|
+
}
|
|
941
|
+
const result = (await response.json());
|
|
942
|
+
if (options.format === "json") {
|
|
943
|
+
out.json(result);
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
spinner.succeed(`Checked child tenant admin access for ${chalk.cyan(userEmail || userOid)}`);
|
|
947
|
+
for (const message of buildTenantBootstrapAdminStatusMessages(result)) {
|
|
948
|
+
if (message.startsWith("Usable: not yet confirmed")) {
|
|
949
|
+
out.warn(message);
|
|
950
|
+
}
|
|
951
|
+
else if (message.startsWith("Usable:")) {
|
|
952
|
+
out.success(message);
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
out.info(message);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
catch (err) {
|
|
961
|
+
if (spinner)
|
|
962
|
+
spinner.fail(err instanceof Error ? err.message : String(err));
|
|
963
|
+
process.exit(1);
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
tenantCommand
|
|
967
|
+
.command("delete <id>")
|
|
968
|
+
.description("Delete a tenant")
|
|
969
|
+
.option("--force", "Skip confirmation", false)
|
|
970
|
+
.option("--format <format>", "Output format (text|json)", "text")
|
|
971
|
+
.option("--json", "Output raw JSON (deprecated, use --format json)", false)
|
|
972
|
+
.action(async (id, options) => {
|
|
973
|
+
if (options.json)
|
|
974
|
+
options.format = "json";
|
|
975
|
+
if (!options.force) {
|
|
976
|
+
const { default: inquirer } = await import("inquirer");
|
|
977
|
+
const { confirm } = await inquirer.prompt([
|
|
978
|
+
{
|
|
979
|
+
type: "confirm",
|
|
980
|
+
name: "confirm",
|
|
981
|
+
message: `Delete tenant ${id}?`,
|
|
982
|
+
default: false,
|
|
983
|
+
},
|
|
984
|
+
]);
|
|
985
|
+
if (!confirm) {
|
|
986
|
+
if (options.format === "json") {
|
|
987
|
+
out.json({ cancelled: true });
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
out.info("Cancelled.");
|
|
991
|
+
}
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
const root = await findProjectRoot();
|
|
996
|
+
const publicApiUrl = await resolvePublicApiUrl(root || undefined);
|
|
997
|
+
const context = await resolveActiveTenantContext({
|
|
998
|
+
projectRoot: root || undefined,
|
|
999
|
+
publicApiUrl,
|
|
1000
|
+
interactive: true,
|
|
1001
|
+
});
|
|
1002
|
+
const client = new PlatformAPIClient(publicApiUrl, context.activeTenant.id);
|
|
1003
|
+
const spinner = options.format === "json"
|
|
1004
|
+
? null
|
|
1005
|
+
: ora(`Deleting tenant "${id}"...`).start();
|
|
1006
|
+
try {
|
|
1007
|
+
const res = await client.deleteTenant(id);
|
|
1008
|
+
if (!res.ok) {
|
|
1009
|
+
const body = await res.text();
|
|
1010
|
+
if (options.format === "json") {
|
|
1011
|
+
out.json({
|
|
1012
|
+
id,
|
|
1013
|
+
deleted: false,
|
|
1014
|
+
status: res.status,
|
|
1015
|
+
error: body || res.statusText,
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
else if (spinner) {
|
|
1019
|
+
spinner.fail(`${res.status}: ${body}`);
|
|
1020
|
+
}
|
|
1021
|
+
process.exit(1);
|
|
1022
|
+
}
|
|
1023
|
+
if (options.format === "json") {
|
|
1024
|
+
out.json({ id, deleted: true });
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
spinner.succeed(`Deleted tenant ${chalk.cyan(id)}`);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
catch (err) {
|
|
1031
|
+
if (spinner)
|
|
1032
|
+
spinner.fail(err instanceof Error ? err.message : String(err));
|
|
1033
|
+
process.exit(1);
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
//# sourceMappingURL=tenant.js.map
|