sofia-cli 0.1.2 → 0.1.4

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.
Files changed (136) hide show
  1. package/README.md +42 -20
  2. package/dist/infra/deploy.sh +193 -0
  3. package/dist/infra/gather-env.sh +211 -0
  4. package/dist/infra/infra/deploy.sh +193 -0
  5. package/dist/infra/infra/gather-env.sh +211 -0
  6. package/dist/infra/infra/main.bicep +90 -0
  7. package/dist/infra/infra/main.bicepparam +18 -0
  8. package/dist/infra/infra/resources.bicep +134 -0
  9. package/dist/infra/infra/teardown.sh +114 -0
  10. package/dist/infra/main.bicep +90 -0
  11. package/dist/infra/main.bicepparam +18 -0
  12. package/dist/infra/resources.bicep +134 -0
  13. package/dist/infra/teardown.sh +114 -0
  14. package/dist/src/cli/developCommand.js +0 -2
  15. package/dist/src/cli/index.js +8 -1
  16. package/dist/src/cli/workshopCommand.js +1 -1
  17. package/dist/src/develop/index.js +1 -1
  18. package/dist/src/develop/pocUtils.js +228 -0
  19. package/dist/src/develop/ralphLoop.js +8 -27
  20. package/dist/src/shared/data/cards.json +655 -670
  21. package/docs/architecture.md +2 -1
  22. package/package.json +5 -3
  23. package/src/cli/developCommand.ts +1 -3
  24. package/src/cli/index.ts +11 -1
  25. package/src/cli/workshopCommand.ts +21 -17
  26. package/src/develop/dynamicScaffolder.ts +36 -30
  27. package/src/develop/index.ts +13 -2
  28. package/src/develop/pocUtils.ts +296 -0
  29. package/src/develop/ralphLoop.ts +8 -28
  30. package/src/develop/templateRegistry.ts +19 -18
  31. package/src/shared/data/cards.json +655 -670
  32. package/tests/e2e/developE2e.spec.ts +3 -61
  33. package/tests/e2e/developFailureE2e.spec.ts +34 -38
  34. package/tests/integration/pocGithubMcp.spec.ts +29 -39
  35. package/tests/integration/pocLocalFallback.spec.ts +29 -39
  36. package/tests/integration/ralphLoopFlow.spec.ts +46 -66
  37. package/tests/integration/ralphLoopPartial.spec.ts +30 -37
  38. package/tests/unit/develop/githubMcpAdapter.spec.ts +0 -134
  39. package/tests/unit/develop/outputValidator.spec.ts +45 -21
  40. package/tests/unit/develop/ralphLoop.spec.ts +58 -94
  41. package/tsconfig.json +2 -1
  42. package/vitest.workspace.ts +5 -0
  43. package/dist/src/develop/pocScaffolder.js +0 -542
  44. package/dist/tests/e2e/developE2e.spec.js +0 -126
  45. package/dist/tests/e2e/developFailureE2e.spec.js +0 -247
  46. package/dist/tests/e2e/developPty.spec.js +0 -75
  47. package/dist/tests/e2e/discoveryWebSearchRelevance.spec.js +0 -84
  48. package/dist/tests/e2e/harness.spec.js +0 -83
  49. package/dist/tests/e2e/mcpLive.spec.js +0 -120
  50. package/dist/tests/e2e/newSession.e2e.spec.js +0 -177
  51. package/dist/tests/e2e/ralphLoopEnrichmentComparison.spec.js +0 -62
  52. package/dist/tests/e2e/workiqEnrichment.spec.js +0 -56
  53. package/dist/tests/e2e/zavaSimulation.spec.js +0 -452
  54. package/dist/tests/fixtures/test-fixture-project/src/add.js +0 -3
  55. package/dist/tests/fixtures/test-fixture-project/tests/failing.test.js +0 -6
  56. package/dist/tests/fixtures/test-fixture-project/tests/hanging.test.js +0 -8
  57. package/dist/tests/fixtures/test-fixture-project/tests/passing.test.js +0 -10
  58. package/dist/tests/fixtures/test-fixture-project/vitest.config.js +0 -6
  59. package/dist/tests/integration/autoStartConversation.spec.js +0 -138
  60. package/dist/tests/integration/defaultCommand.spec.js +0 -147
  61. package/dist/tests/integration/directCommandNonTty.spec.js +0 -224
  62. package/dist/tests/integration/directCommandTty.spec.js +0 -151
  63. package/dist/tests/integration/discoveryEnrichmentFlow.spec.js +0 -175
  64. package/dist/tests/integration/exportArtifacts.spec.js +0 -202
  65. package/dist/tests/integration/exportFallbackFlow.spec.js +0 -99
  66. package/dist/tests/integration/mcpDegradationFlow.spec.js +0 -190
  67. package/dist/tests/integration/mcpTransportFlow.spec.js +0 -139
  68. package/dist/tests/integration/newSessionFlow.spec.js +0 -343
  69. package/dist/tests/integration/pocGithubMcp.spec.js +0 -186
  70. package/dist/tests/integration/pocLocalFallback.spec.js +0 -171
  71. package/dist/tests/integration/pocScaffold.spec.js +0 -163
  72. package/dist/tests/integration/ralphLoopFlow.spec.js +0 -359
  73. package/dist/tests/integration/ralphLoopPartial.spec.js +0 -368
  74. package/dist/tests/integration/resumeAndBacktrack.spec.js +0 -247
  75. package/dist/tests/integration/spinnerLifecycle.spec.js +0 -220
  76. package/dist/tests/integration/summarizationFlow.spec.js +0 -115
  77. package/dist/tests/integration/testRunnerReal.spec.js +0 -52
  78. package/dist/tests/integration/webSearchAgent.spec.js +0 -128
  79. package/dist/tests/live/copilotSdkLive.spec.js +0 -107
  80. package/dist/tests/live/zavaFullWorkshop.spec.js +0 -392
  81. package/dist/tests/setup/loadEnv.js +0 -3
  82. package/dist/tests/unit/cli/developCommand.spec.js +0 -567
  83. package/dist/tests/unit/cli/directCommands.spec.js +0 -279
  84. package/dist/tests/unit/cli/envLoader.spec.js +0 -58
  85. package/dist/tests/unit/cli/ioContext.spec.js +0 -119
  86. package/dist/tests/unit/cli/preflight.spec.js +0 -108
  87. package/dist/tests/unit/cli/statusCommand.spec.js +0 -111
  88. package/dist/tests/unit/cli/workshopClientFallback.spec.js +0 -80
  89. package/dist/tests/unit/cli/workshopCommand.spec.js +0 -328
  90. package/dist/tests/unit/config/vitestEnvSetup.spec.js +0 -13
  91. package/dist/tests/unit/develop/checkpointState.spec.js +0 -315
  92. package/dist/tests/unit/develop/codeGenerator.spec.js +0 -355
  93. package/dist/tests/unit/develop/githubMcpAdapter.spec.js +0 -231
  94. package/dist/tests/unit/develop/mcpContextEnricher.spec.js +0 -433
  95. package/dist/tests/unit/develop/outputValidator.spec.js +0 -119
  96. package/dist/tests/unit/develop/pocScaffolder.spec.js +0 -353
  97. package/dist/tests/unit/develop/ralphLoop.spec.js +0 -1248
  98. package/dist/tests/unit/develop/templateRegistry.spec.js +0 -85
  99. package/dist/tests/unit/develop/testRunner.spec.js +0 -249
  100. package/dist/tests/unit/infraBicep.spec.js +0 -92
  101. package/dist/tests/unit/infraDeploy.spec.js +0 -82
  102. package/dist/tests/unit/infraTeardown.spec.js +0 -63
  103. package/dist/tests/unit/logging/logger.spec.js +0 -43
  104. package/dist/tests/unit/loop/conversationLoop.spec.js +0 -592
  105. package/dist/tests/unit/loop/phaseSummarizer.spec.js +0 -141
  106. package/dist/tests/unit/loop/streamingMarkdown.spec.js +0 -147
  107. package/dist/tests/unit/mcp/mcpManager.spec.js +0 -279
  108. package/dist/tests/unit/mcp/mcpTransport.spec.js +0 -529
  109. package/dist/tests/unit/mcp/retryPolicy.spec.js +0 -218
  110. package/dist/tests/unit/mcp/timeoutValidation.spec.js +0 -46
  111. package/dist/tests/unit/mcp/webSearch.spec.js +0 -567
  112. package/dist/tests/unit/phases/contextSummarizer.spec.js +0 -140
  113. package/dist/tests/unit/phases/discoveryEnricher.repeatCalls.spec.js +0 -93
  114. package/dist/tests/unit/phases/discoveryEnricher.spec.js +0 -411
  115. package/dist/tests/unit/phases/phaseExtractors.spec.js +0 -352
  116. package/dist/tests/unit/phases/phaseHandlers.spec.js +0 -425
  117. package/dist/tests/unit/prompts/promptLoader.spec.js +0 -118
  118. package/dist/tests/unit/schemas/pocSchemas.spec.js +0 -412
  119. package/dist/tests/unit/schemas/session.spec.js +0 -257
  120. package/dist/tests/unit/sessions/exportPaths.spec.js +0 -31
  121. package/dist/tests/unit/sessions/exportWriter.spec.js +0 -655
  122. package/dist/tests/unit/sessions/sessionManager.spec.js +0 -151
  123. package/dist/tests/unit/sessions/sessionStore.spec.js +0 -116
  124. package/dist/tests/unit/shared/activitySpinner.spec.js +0 -175
  125. package/dist/tests/unit/shared/cardsLoader.spec.js +0 -76
  126. package/dist/tests/unit/shared/copilotClient.spec.js +0 -155
  127. package/dist/tests/unit/shared/errorClassifier.spec.js +0 -131
  128. package/dist/tests/unit/shared/events.spec.js +0 -55
  129. package/dist/tests/unit/shared/markdownRenderer.spec.js +0 -35
  130. package/dist/tests/unit/shared/markdownRendererChunks.spec.js +0 -70
  131. package/dist/tests/unit/shared/tableRenderer.spec.js +0 -34
  132. package/dist/vitest.config.js +0 -14
  133. package/dist/vitest.live.config.js +0 -18
  134. package/src/develop/pocScaffolder.ts +0 -646
  135. package/tests/integration/pocScaffold.spec.ts +0 -220
  136. package/tests/unit/develop/pocScaffolder.spec.ts +0 -451
package/README.md CHANGED
@@ -69,22 +69,34 @@ sofIA uses [Model Context Protocol](https://modelcontextprotocol.io/) servers to
69
69
  ```bash
70
70
  # Prerequisites: Node.js >= 20, a GitHub Copilot subscription
71
71
 
72
- # Clone & install
73
- git clone https://github.com/jmservera/sofia-cli.git
74
- cd sofia-cli
75
- npm install
72
+ # Install globally from npm
73
+ npm install -g sofia-cli
74
+
75
+ # Or run directly with npx (no install required)
76
+ npx sofia-cli workshop
76
77
 
77
78
  # Start an interactive workshop
78
- npm run start -- workshop
79
+ sofia workshop
79
80
 
80
81
  # Check session status
81
- npm run start -- status
82
+ sofia status
82
83
 
83
84
  # Generate a PoC from a completed session
84
- npm run start -- dev --session <id>
85
+ sofia dev --session <id>
85
86
 
86
87
  # Export workshop artifacts
87
- npm run start -- export --session <id>
88
+ sofia export --session <id>
89
+ ```
90
+
91
+ ### Development Setup
92
+
93
+ If you want to contribute or run from source:
94
+
95
+ ```bash
96
+ git clone https://github.com/jmservera/sofia-cli.git
97
+ cd sofia-cli
98
+ npm install
99
+ npm run start -- workshop
88
100
  ```
89
101
 
90
102
  ## CLI Commands
@@ -111,24 +123,28 @@ infra/ # Azure AI Foundry IaC (Bicep + deployment scripts)
111
123
  src/
112
124
  ├── cli/ # Command handlers (workshop, dev, status, export)
113
125
  ├── develop/ # PoC generation & Ralph loop
114
- │ ├── ralphLoop.ts # Autonomous iteration orchestrator
115
- │ ├── pocScaffolder.ts # Project scaffolding
116
- │ ├── codeGenerator.ts # LLM-driven code generation
117
- │ ├── testRunner.ts # Test execution & result parsing
126
+ │ ├── ralphLoop.ts # Autonomous iteration orchestrator
127
+ │ ├── dynamicScaffolder.ts # LLM-based project scaffolding
128
+ │ ├── codeGenerator.ts # LLM-driven code generation
129
+ │ ├── testRunner.ts # Test execution & result parsing
130
+ │ ├── checkpointState.ts # Resume/checkpoint detection
118
131
  │ └── ...
119
132
  ├── loop/ # Multi-turn conversation orchestrator
120
133
  ├── phases/ # Phase handlers & JSON extractors
121
134
  ├── prompts/ # Versioned prompt templates
122
135
  ├── sessions/ # Session persistence & export
123
- ├── mcp/ # MCP server connection manager
136
+ ├── mcp/ # MCP server connection manager & transports
124
137
  ├── shared/ # Schemas, events, error classification
125
138
  │ └── schemas/ # Zod schemas for session state
126
139
  └── vendor/ # Re-exports of external packages
127
140
 
128
141
  specs/
129
- ├── 001-cli-workshop-rebuild/ # Workshop phases spec & tasks
130
- ├── 002-poc-generation/ # Develop phase spec & tasks
131
- └── 003-next-spec-gaps.md # Outstanding gaps for next feature
142
+ ├── 001-cli-workshop-rebuild/ # Workshop phases orchestration
143
+ ├── 002-poc-generation/ # Develop phase & Ralph loop
144
+ ├── 003-mcp-transport-integration/ # Real MCP server connections
145
+ ├── 004-dev-resume-hardening/ # Checkpoint, resume, --force
146
+ ├── 005-ai-search-deploy/ # Azure AI Foundry infrastructure
147
+ └── 006-workshop-extraction-fixes/ # Extraction, wiring, context mgmt
132
148
 
133
149
  docs/ # User-facing documentation
134
150
  tests/
@@ -183,10 +199,16 @@ See [.github/copilot-instructions.md](.github/copilot-instructions.md) for detai
183
199
 
184
200
  ## Current Status
185
201
 
186
- - **Feature 001** (Workshop CLI): Discover → Ideate → Design → Select → Plan — **implemented and tested**
187
- - **Feature 002** (PoC Generation): Develop phase with Ralph loop — **implemented and tested** (MCP integrations are simulated; see [specs/003-next-spec-gaps.md](specs/003-next-spec-gaps.md))
188
- - **Feature 003** (MCP Integration): Real MCP tool invocation, resume/checkpoint, additional templates — **planned**
189
- - **Feature 005** (AI Search Deploy): Azure AI Foundry infrastructure deployment + SDK-based web search integration — **implemented and tested**
202
+ All six features have been implemented and tested:
203
+
204
+ | Feature | Title | Description | Status |
205
+ | ------- | --------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------- |
206
+ | 001 | [Workshop Rebuild](specs/001-cli-workshop-rebuild/spec.md) | 5-phase orchestration (Discover → Plan), session persistence, export | **Implemented** |
207
+ | 002 | [PoC Generation](specs/002-poc-generation/spec.md) | Develop phase with Ralph loop, LLM-based dynamic scaffolding | **Implemented** |
208
+ | 003 | [MCP Transport](specs/003-mcp-transport-integration/spec.md) | Real MCP tool invocation (GitHub, Context7, Azure, web search, WorkIQ) | **Implemented** |
209
+ | 004 | [Dev Hardening](specs/004-dev-resume-hardening/spec.md) | Checkpoint/resume, `--force` flag, template registry, test coverage | **Implemented** |
210
+ | 005 | [Search Deployment](specs/005-ai-search-deploy/spec.md) | Azure AI Foundry Bicep infrastructure with one-command deploy/teardown | **Implemented** |
211
+ | 006 | [Extraction Fixes](specs/006-workshop-extraction-fixes/spec.md) | Fallback extraction, MCP wiring per phase, context window management | **Implemented** |
190
212
 
191
213
  ## Web Search Setup
192
214
 
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env bash
2
+ # ──────────────────────────────────────────────────────────────────────────────
3
+ # deploy.sh — One-command Azure AI Foundry deployment for sofIA
4
+ #
5
+ # Provisions:
6
+ # - Resource group (auto-created)
7
+ # - AI Services account with Foundry project
8
+ # - Model deployment (gpt-4.1-mini by default)
9
+ # - Agent Service capability hosts
10
+ #
11
+ # Usage:
12
+ # ./infra/deploy.sh --resource-group <name> [options]
13
+ #
14
+ # Exit codes:
15
+ # 0 — Deployment succeeded
16
+ # 1 — Prerequisite check failed
17
+ # 2 — Deployment failed
18
+ # ──────────────────────────────────────────────────────────────────────────────
19
+ set -euo pipefail
20
+
21
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22
+
23
+ # ── Defaults ──────────────────────────────────────────────────────────────────
24
+
25
+ SUBSCRIPTION=""
26
+ RESOURCE_GROUP=""
27
+ LOCATION="swedencentral"
28
+ ACCOUNT_NAME="sofia-foundry"
29
+ MODEL="gpt-4.1-mini"
30
+
31
+ # ── Parameter parsing ─────────────────────────────────────────────────────────
32
+
33
+ usage() {
34
+ cat <<EOF
35
+ Usage: $(basename "$0") [options]
36
+
37
+ Required:
38
+ -g, --resource-group <name> Resource group name (created if missing)
39
+
40
+ Optional:
41
+ -s, --subscription <id> Azure subscription ID (default: current az CLI subscription)
42
+ -l, --location <region> Azure region (default: swedencentral)
43
+ -n, --account-name <name> Foundry account name (default: sofia-foundry)
44
+ -m, --model <name> Model deployment name (default: gpt-4.1-mini)
45
+ -h, --help Show this help message
46
+ EOF
47
+ }
48
+
49
+ while [[ $# -gt 0 ]]; do
50
+ case "$1" in
51
+ -s|--subscription)
52
+ SUBSCRIPTION="$2"; shift 2 ;;
53
+ -g|--resource-group)
54
+ RESOURCE_GROUP="$2"; shift 2 ;;
55
+ -l|--location)
56
+ LOCATION="$2"; shift 2 ;;
57
+ -n|--account-name)
58
+ ACCOUNT_NAME="$2"; shift 2 ;;
59
+ -m|--model)
60
+ MODEL="$2"; shift 2 ;;
61
+ -h|--help)
62
+ usage; exit 0 ;;
63
+ *)
64
+ echo "❌ Unknown option: $1" >&2
65
+ usage >&2
66
+ exit 1 ;;
67
+ esac
68
+ done
69
+
70
+ # ── Validate required parameters ─────────────────────────────────────────────
71
+
72
+ if [[ -z "$RESOURCE_GROUP" ]]; then
73
+ echo "❌ Missing required parameter: --resource-group (-g)" >&2
74
+ usage >&2
75
+ exit 1
76
+ fi
77
+
78
+ # ── Prerequisite checks ──────────────────────────────────────────────────────
79
+
80
+ echo "🔍 Checking prerequisites..."
81
+
82
+ # Check az CLI is installed
83
+ if ! command -v az &>/dev/null; then
84
+ echo "❌ Azure CLI (az) is not installed." >&2
85
+ echo " Install: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli" >&2
86
+ exit 1
87
+ fi
88
+
89
+ # Check user is logged in
90
+ if ! az account show &>/dev/null; then
91
+ echo "❌ Not logged in to Azure. Run 'az login' first." >&2
92
+ exit 1
93
+ fi
94
+
95
+ # Optionally set subscription
96
+ if [[ -n "$SUBSCRIPTION" ]]; then
97
+ echo "📋 Setting subscription to: $SUBSCRIPTION"
98
+ if ! az account set --subscription "$SUBSCRIPTION" 2>/dev/null; then
99
+ echo "❌ Could not set subscription '$SUBSCRIPTION'." >&2
100
+ echo " Check the subscription ID and your permissions." >&2
101
+ exit 1
102
+ fi
103
+ fi
104
+
105
+ echo "✅ Prerequisites passed"
106
+
107
+ # ── Resolve current user's principal (object) ID for RBAC ─────────────────────
108
+
109
+ echo "🔑 Resolving current user's principal ID..."
110
+ USER_PRINCIPAL_ID=$(az ad signed-in-user show --query id -o tsv 2>/dev/null || true)
111
+ if [[ -z "$USER_PRINCIPAL_ID" ]]; then
112
+ echo "⚠️ Could not resolve user principal ID. Role assignment will be skipped." >&2
113
+ echo " You may need to manually assign the 'Azure AI Developer' role." >&2
114
+ else
115
+ echo " Principal ID: $USER_PRINCIPAL_ID"
116
+ fi
117
+
118
+ # ── Deploy ────────────────────────────────────────────────────────────────────
119
+
120
+ echo ""
121
+ echo "🚀 Deploying Azure AI Foundry infrastructure..."
122
+ echo " Resource Group: $RESOURCE_GROUP"
123
+ echo " Location: $LOCATION"
124
+ echo " Account: $ACCOUNT_NAME"
125
+ echo " Model: $MODEL"
126
+ echo ""
127
+
128
+ DEPLOYMENT_NAME="sofia-foundry-$(date +%Y%m%d%H%M%S)"
129
+
130
+ if ! az deployment sub create \
131
+ --location "$LOCATION" \
132
+ --name "$DEPLOYMENT_NAME" \
133
+ --template-file "$SCRIPT_DIR/main.bicep" \
134
+ --parameters "$SCRIPT_DIR/main.bicepparam" \
135
+ --parameters \
136
+ resourceGroupName="$RESOURCE_GROUP" \
137
+ location="$LOCATION" \
138
+ accountName="$ACCOUNT_NAME" \
139
+ modelDeploymentName="$MODEL" \
140
+ modelName="$MODEL" \
141
+ userPrincipalId="$USER_PRINCIPAL_ID" \
142
+ --output json; then
143
+ echo "" >&2
144
+ echo "❌ Deployment failed." >&2
145
+ echo " Check the error above for details." >&2
146
+ echo " Common issues:" >&2
147
+ echo " - Insufficient permissions (need Owner or Contributor)" >&2
148
+ echo " - Region doesn't support AI Foundry Agent Service" >&2
149
+ echo " - Resource name conflict (try a different --account-name)" >&2
150
+ exit 2
151
+ fi
152
+
153
+ # ── Query outputs ─────────────────────────────────────────────────────────────
154
+
155
+ PROJECT_ENDPOINT=$(az deployment sub show \
156
+ --name "$DEPLOYMENT_NAME" \
157
+ --query "properties.outputs.projectEndpoint.value" \
158
+ --output tsv 2>/dev/null || echo "")
159
+
160
+ MODEL_DEPLOYMENT_NAME=$(az deployment sub show \
161
+ --name "$DEPLOYMENT_NAME" \
162
+ --query "properties.outputs.modelDeploymentName.value" \
163
+ --output tsv 2>/dev/null || echo "$MODEL")
164
+
165
+ # ── Write .env file ───────────────────────────────────────────────────────────
166
+
167
+ ENV_FILE="$SCRIPT_DIR/../.env"
168
+
169
+ # Helper: set or update a KEY=VALUE in the .env file
170
+ set_env_var() {
171
+ local key="$1" value="$2"
172
+ if [[ -f "$ENV_FILE" ]] && grep -q "^${key}=" "$ENV_FILE"; then
173
+ # Update existing entry (portable sed -i)
174
+ sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE"
175
+ else
176
+ echo "${key}=\"${value}\"" >> "$ENV_FILE"
177
+ fi
178
+ }
179
+
180
+ set_env_var "FOUNDRY_PROJECT_ENDPOINT" "$PROJECT_ENDPOINT"
181
+ set_env_var "FOUNDRY_MODEL_DEPLOYMENT_NAME" "$MODEL_DEPLOYMENT_NAME"
182
+
183
+ # ── Output ────────────────────────────────────────────────────────────────────
184
+
185
+ echo ""
186
+ echo "✅ Deployment complete!"
187
+ echo ""
188
+ echo "Environment variables written to $(realpath "$ENV_FILE"):"
189
+ echo ""
190
+ echo " FOUNDRY_PROJECT_ENDPOINT=\"$PROJECT_ENDPOINT\""
191
+ echo " FOUNDRY_MODEL_DEPLOYMENT_NAME=\"$MODEL_DEPLOYMENT_NAME\""
192
+ echo ""
193
+ echo "To tear down: ./infra/teardown.sh --resource-group $RESOURCE_GROUP"
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env bash
2
+ # ──────────────────────────────────────────────────────────────────────────────
3
+ # gather-env.sh — Fetch environment values from an existing Azure AI Foundry
4
+ # resource group without redeploying.
5
+ #
6
+ # Queries the AI Services account and project already provisioned by deploy.sh,
7
+ # then writes (or updates) the .env file with the same variables.
8
+ #
9
+ # Usage:
10
+ # ./infra/gather-env.sh --resource-group <name> [options]
11
+ #
12
+ # Exit codes:
13
+ # 0 — Values gathered and written successfully
14
+ # 1 — Prerequisite / parameter check failed
15
+ # 2 — Resource query failed
16
+ # ──────────────────────────────────────────────────────────────────────────────
17
+ set -euo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+
21
+ # ── Defaults ──────────────────────────────────────────────────────────────────
22
+
23
+ SUBSCRIPTION=""
24
+ RESOURCE_GROUP=""
25
+ ACCOUNT_NAME="sofia-foundry"
26
+ MODEL=""
27
+
28
+ # ── Parameter parsing ─────────────────────────────────────────────────────────
29
+
30
+ usage() {
31
+ cat <<EOF
32
+ Usage: $(basename "$0") [options]
33
+
34
+ Required:
35
+ -g, --resource-group <name> Resource group containing the Foundry resources
36
+
37
+ Optional:
38
+ -s, --subscription <id> Azure subscription ID (default: current az CLI subscription)
39
+ -n, --account-name <name> AI Services account name (default: sofia-foundry)
40
+ -m, --model <name> Override model deployment name (default: auto-detected)
41
+ -h, --help Show this help message
42
+ EOF
43
+ }
44
+
45
+ while [[ $# -gt 0 ]]; do
46
+ case "$1" in
47
+ -s|--subscription)
48
+ SUBSCRIPTION="$2"; shift 2 ;;
49
+ -g|--resource-group)
50
+ RESOURCE_GROUP="$2"; shift 2 ;;
51
+ -n|--account-name)
52
+ ACCOUNT_NAME="$2"; shift 2 ;;
53
+ -m|--model)
54
+ MODEL="$2"; shift 2 ;;
55
+ -h|--help)
56
+ usage; exit 0 ;;
57
+ *)
58
+ echo "❌ Unknown option: $1" >&2
59
+ usage >&2
60
+ exit 1 ;;
61
+ esac
62
+ done
63
+
64
+ # ── Validate required parameters ─────────────────────────────────────────────
65
+
66
+ if [[ -z "$RESOURCE_GROUP" ]]; then
67
+ echo "❌ Missing required parameter: --resource-group (-g)" >&2
68
+ usage >&2
69
+ exit 1
70
+ fi
71
+
72
+ # ── Prerequisite checks ──────────────────────────────────────────────────────
73
+
74
+ echo "🔍 Checking prerequisites..."
75
+
76
+ if ! command -v az &>/dev/null; then
77
+ echo "❌ Azure CLI (az) is not installed." >&2
78
+ echo " Install: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli" >&2
79
+ exit 1
80
+ fi
81
+
82
+ if ! az account show &>/dev/null; then
83
+ echo "❌ Not logged in to Azure. Run 'az login' first." >&2
84
+ exit 1
85
+ fi
86
+
87
+ if [[ -n "$SUBSCRIPTION" ]]; then
88
+ echo "📋 Setting subscription to: $SUBSCRIPTION"
89
+ if ! az account set --subscription "$SUBSCRIPTION" 2>/dev/null; then
90
+ echo "❌ Could not set subscription '$SUBSCRIPTION'." >&2
91
+ exit 1
92
+ fi
93
+ fi
94
+
95
+ echo "✅ Prerequisites passed"
96
+
97
+ # ── Verify resource group exists ──────────────────────────────────────────────
98
+
99
+ echo ""
100
+ echo "🔍 Verifying resource group: $RESOURCE_GROUP"
101
+
102
+ if ! az group show --name "$RESOURCE_GROUP" &>/dev/null; then
103
+ echo "❌ Resource group '$RESOURCE_GROUP' not found." >&2
104
+ echo " Run deploy.sh first to create the infrastructure." >&2
105
+ exit 2
106
+ fi
107
+
108
+ # ── Find the AI Services account ─────────────────────────────────────────────
109
+
110
+ echo "🔍 Looking up AI Services account: $ACCOUNT_NAME"
111
+
112
+ if ! az cognitiveservices account show \
113
+ --resource-group "$RESOURCE_GROUP" \
114
+ --name "$ACCOUNT_NAME" &>/dev/null; then
115
+ echo "⚠️ Account '$ACCOUNT_NAME' not found. Searching for AI Services accounts in the resource group..."
116
+ ACCOUNT_NAME=$(az cognitiveservices account list \
117
+ --resource-group "$RESOURCE_GROUP" \
118
+ --query "[?kind=='AIServices'].name | [0]" \
119
+ --output tsv 2>/dev/null || true)
120
+
121
+ if [[ -z "$ACCOUNT_NAME" ]]; then
122
+ echo "❌ No AI Services account found in resource group '$RESOURCE_GROUP'." >&2
123
+ exit 2
124
+ fi
125
+ echo " Found account: $ACCOUNT_NAME"
126
+ fi
127
+
128
+ # ── Find the project and its endpoint ────────────────────────────────────────
129
+
130
+ echo "🔍 Looking up Foundry project..."
131
+
132
+ PROJECT_ENDPOINT=$(az cognitiveservices account show \
133
+ --resource-group "$RESOURCE_GROUP" \
134
+ --name "$ACCOUNT_NAME" \
135
+ --query "properties.endpoints.\"AI Foundry API\"" \
136
+ --output tsv 2>/dev/null || true)
137
+
138
+ # If the account-level endpoint isn't what we need, try querying the project
139
+ if [[ -z "$PROJECT_ENDPOINT" ]]; then
140
+ # List projects under the account
141
+ PROJECT_NAME=$(az resource list \
142
+ --resource-group "$RESOURCE_GROUP" \
143
+ --resource-type "Microsoft.CognitiveServices/accounts/projects" \
144
+ --query "[0].name" \
145
+ --output tsv 2>/dev/null || true)
146
+
147
+ if [[ -n "$PROJECT_NAME" ]]; then
148
+ # The resource name is "account/project", extract just the project part
149
+ LOCAL_PROJECT="${PROJECT_NAME##*/}"
150
+ echo " Found project: $LOCAL_PROJECT"
151
+
152
+ PROJECT_ENDPOINT=$(az rest \
153
+ --method GET \
154
+ --uri "https://management.azure.com/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.CognitiveServices/accounts/$ACCOUNT_NAME/projects/$LOCAL_PROJECT?api-version=2025-06-01" \
155
+ --query "properties.endpoints.\"AI Foundry API\"" \
156
+ --output tsv 2>/dev/null || true)
157
+ fi
158
+ fi
159
+
160
+ if [[ -z "$PROJECT_ENDPOINT" ]]; then
161
+ echo "❌ Could not determine the Foundry project endpoint." >&2
162
+ echo " Ensure a project exists under account '$ACCOUNT_NAME'." >&2
163
+ exit 2
164
+ fi
165
+
166
+ echo " Endpoint: $PROJECT_ENDPOINT"
167
+
168
+ # ── Find the model deployment name ───────────────────────────────────────────
169
+
170
+ if [[ -z "$MODEL" ]]; then
171
+ echo "🔍 Looking up model deployment..."
172
+ MODEL=$(az cognitiveservices account deployment list \
173
+ --resource-group "$RESOURCE_GROUP" \
174
+ --name "$ACCOUNT_NAME" \
175
+ --query "[0].name" \
176
+ --output tsv 2>/dev/null || true)
177
+
178
+ if [[ -z "$MODEL" ]]; then
179
+ echo "⚠️ No model deployment found. Using default: gpt-4.1-mini"
180
+ MODEL="gpt-4.1-mini"
181
+ fi
182
+ fi
183
+
184
+ echo " Model deployment: $MODEL"
185
+
186
+ # ── Write .env file ───────────────────────────────────────────────────────────
187
+
188
+ ENV_FILE="$SCRIPT_DIR/../.env"
189
+
190
+ set_env_var() {
191
+ local key="$1" value="$2"
192
+ if [[ -f "$ENV_FILE" ]] && grep -q "^${key}=" "$ENV_FILE"; then
193
+ sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$ENV_FILE"
194
+ else
195
+ echo "${key}=\"${value}\"" >> "$ENV_FILE"
196
+ fi
197
+ }
198
+
199
+ set_env_var "FOUNDRY_PROJECT_ENDPOINT" "$PROJECT_ENDPOINT"
200
+ set_env_var "FOUNDRY_MODEL_DEPLOYMENT_NAME" "$MODEL"
201
+
202
+ # ── Output ────────────────────────────────────────────────────────────────────
203
+
204
+ echo ""
205
+ echo "✅ Environment values gathered successfully!"
206
+ echo ""
207
+ echo "Written to $(realpath "$ENV_FILE"):"
208
+ echo ""
209
+ echo " FOUNDRY_PROJECT_ENDPOINT=\"$PROJECT_ENDPOINT\""
210
+ echo " FOUNDRY_MODEL_DEPLOYMENT_NAME=\"$MODEL\""
211
+ echo ""