Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/smoke-copilot-byok.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions .github/workflows/smoke-copilot-byok.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ steps:
echo "::group::Fetching last 2 merged PRs"
PR_DATA=$(gh pr list --repo "$GITHUB_REPOSITORY" --state merged --limit 2 \
--json number,title,author,mergedAt \
--jq '.[] | "PR #\(.number): \(.title) (by @\(.author.login), merged \(.mergedAt))"')
--jq '.[] | "PR #\(.number): \(.title) (by @\(.author.login), merged \(.mergedAt))"' \
|| echo "(PR fetch failed)")
echo "$PR_DATA"
echo "::endgroup::"

echo "::group::GitHub.com connectivity check"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 https://github.com)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 https://github.com || echo "000")
echo "github.com returned HTTP $HTTP_CODE"
echo "::endgroup::"

Expand Down
35 changes: 34 additions & 1 deletion containers/api-proxy/providers/anthropic.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,17 @@ function createAnthropicAdapter(env, deps = {}) {
name: 'anthropic',
port: 10001,
isManagementPort: false,
alwaysBind: false,

/**
* Port 10001 always starts so agents get a clear 503 "not configured"
* error rather than a silent connection-refused.
*/
alwaysBind: true,

/**
* The stub server does NOT count toward the startup validation latch —
* only the fully-configured server (when ANTHROPIC_API_KEY is set) does.
*/
get participatesInValidation() { return this.isEnabled(); },

isEnabled() { return !!apiKey; },
Expand Down Expand Up @@ -159,6 +169,29 @@ function createAnthropicAdapter(env, deps = {}) {
};
},

/** Response returned for all requests when no ANTHROPIC_API_KEY is configured. */
getUnconfiguredResponse() {
return {
statusCode: 503,
body: {
error: {
message: 'Credentials for Anthropic (port 10001) are not configured. Set ANTHROPIC_API_KEY to enable this provider.',
type: 'provider_not_configured',
provider: 'anthropic',
port: 10001,
},
},
};
},

/** /health response when not configured. */
getUnconfiguredHealthResponse() {
return {
statusCode: 503,
body: { status: 'not_configured', service: 'awf-api-proxy-anthropic', error: 'ANTHROPIC_API_KEY not configured in api-proxy sidecar' },
};
},

// Exposed for introspection (logging, tests)
_autoCache: autoCache,
_cacheTailTtl: cacheTailTtl,
Expand Down
35 changes: 34 additions & 1 deletion containers/api-proxy/providers/copilot.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,17 @@ function createCopilotAdapter(env, deps = {}) {
name: 'copilot',
port: 10002,
isManagementPort: false,
alwaysBind: false,

/**
* Port 10002 always starts so agents get a clear 503 "not configured"
* error rather than a silent connection-refused.
*/
alwaysBind: true,

/**
* The stub server does NOT count toward the startup validation latch —
* only the fully-configured server (when credentials are present) does.
*/
get participatesInValidation() { return this.isEnabled(); },

isEnabled() { return !!authToken; },
Expand Down Expand Up @@ -252,6 +262,29 @@ function createCopilotAdapter(env, deps = {}) {
};
},

/** Response returned for all requests when no Copilot credentials are configured. */
getUnconfiguredResponse() {
return {
statusCode: 503,
body: {
error: {
message: 'Credentials for GitHub Copilot (port 10002) are not configured. Set COPILOT_GITHUB_TOKEN or COPILOT_API_KEY to enable this provider.',
type: 'provider_not_configured',
provider: 'copilot',
port: 10002,
},
},
};
},

/** /health response when not configured. */
getUnconfiguredHealthResponse() {
return {
statusCode: 503,
body: { status: 'not_configured', service: 'awf-api-proxy-copilot', error: 'COPILOT_GITHUB_TOKEN or COPILOT_API_KEY not configured in api-proxy sidecar' },
};
},

// Exposed for introspection / testing
_githubToken: githubToken,
_apiKey: apiKey,
Expand Down
51 changes: 50 additions & 1 deletion containers/api-proxy/providers/opencode.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,17 @@ function createOpenCodeAdapter(env, { candidateAdapters = [] } = {}) {
name: 'opencode',
port: 10004,
isManagementPort: false,
alwaysBind: false,

/**
* Port 10004 always starts so agents get a clear 503 "not configured"
* error rather than a silent connection-refused.
*/
alwaysBind: true,

/**
* The stub server does NOT count toward the startup validation latch —
* only the fully-configured server (when enabled and a candidate is active) does.
*/
get participatesInValidation() { return this.isEnabled(); },

isEnabled() { return enabled && !!resolveActiveAdapter(); },
Expand Down Expand Up @@ -168,6 +178,45 @@ function createOpenCodeAdapter(env, { candidateAdapters = [] } = {}) {
};
},

/** Response returned for all requests when OpenCode is not configured. */
getUnconfiguredResponse() {
if (!enabled) {
return {
statusCode: 503,
body: {
error: {
message: 'OpenCode proxy (port 10004) is not enabled. Set AWF_ENABLE_OPENCODE=true and configure at least one of OPENAI_API_KEY, ANTHROPIC_API_KEY, COPILOT_GITHUB_TOKEN, or COPILOT_API_KEY.',
type: 'provider_not_configured',
provider: 'opencode',
port: 10004,
},
},
Comment on lines +185 to +193
};
}
return {
statusCode: 503,
body: {
error: {
message: 'Credentials for OpenCode (port 10004) are not configured. Set at least one of OPENAI_API_KEY, ANTHROPIC_API_KEY, COPILOT_GITHUB_TOKEN, or COPILOT_API_KEY.',
type: 'provider_not_configured',
provider: 'opencode',
port: 10004,
},
},
};
},

/** /health response when not configured. */
getUnconfiguredHealthResponse() {
const reason = !enabled
? 'AWF_ENABLE_OPENCODE not set to true'
: 'no candidate provider credentials configured';
return {
statusCode: 503,
body: { status: 'not_configured', service: 'awf-api-proxy-opencode', error: `OpenCode proxy not configured: ${reason}` },
};
},

// Exposed for introspection / testing
_startupActiveAdapterName: startupActiveAdapter?.name || null,
_candidateAdapters: candidateAdapters,
Expand Down
7 changes: 7 additions & 0 deletions containers/api-proxy/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,13 @@ function createProviderServer(adapter) {
return;
}

// ── Provider-local reflect endpoint ──────────────────────────────────────
if (req.url === '/reflect' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(reflectEndpoints()));
return;
}

// ── Disabled adapter: return provider-specific error ─────────────────────
if (!adapter.isEnabled()) {
const response = adapter.getUnconfiguredResponse
Expand Down
Loading
Loading