diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index b032395..d519936 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -69,33 +69,75 @@ jobs: echo "skip=false" >> $GITHUB_OUTPUT fi + - name: Setup Node.js + if: steps.check_changes.outputs.skip != 'true' + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Start Hush Gateway + if: steps.check_changes.outputs.skip != 'true' + run: | + npm install -g @aictrl/hush@0.1.7 + HUSH_BIN="$(npm prefix -g)/bin/hush" + PORT=4000 HUSH_HOST=127.0.0.1 "$HUSH_BIN" & + # Wait for gateway to be ready + for i in $(seq 1 20); do + curl -sf http://127.0.0.1:4000/health > /dev/null 2>&1 && break + sleep 0.5 + done + curl -sf http://127.0.0.1:4000/health || { echo "::error::Hush gateway failed to start"; exit 1; } + echo "Hush gateway running on :4000" + + - name: Build project + if: steps.check_changes.outputs.skip != 'true' + run: npm ci && npm run build + - name: Setup OpenCode if: steps.check_changes.outputs.skip != 'true' env: ZHIPU_API_KEY: ${{ secrets.ZHIPUAI_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - # Use GITHUB_TOKEN to avoid rate limits when fetching version info + # Install OpenCode curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path echo "$HOME/.opencode/bin" >> $GITHUB_PATH + # Configure OpenCode with custom provider routing through hush proxy. + # NOTE: Overriding baseURL on built-in providers (zai-coding-plan) does not + # work — OpenCode's bundled @ai-sdk/openai-compatible ignores options.baseURL + # (see anomalyco/opencode#5674). Instead, we define a new custom provider + # "hush-zhipu" that explicitly sets baseURL via the npm adapter. + mkdir -p .opencode/plugins + cp examples/team-config/.opencode/plugins/hush.ts .opencode/plugins/hush.ts + printf '%s\n' '{"provider":{"hush-zhipu":{"npm":"@ai-sdk/openai-compatible","name":"Hush ZhipuAI Proxy","options":{"baseURL":"http://127.0.0.1:4000/api/coding/paas/v4","apiKey":"{env:ZHIPU_API_KEY}"},"models":{"glm-5":{"name":"GLM 5"},"glm-4.7-flash":{"name":"GLM 4.7 Flash"}}}},"plugin":[".opencode/plugins/hush.ts"]}' > opencode.json + - name: Direct OpenCode Review if: steps.check_changes.outputs.skip != 'true' + timeout-minutes: 15 env: ZHIPU_API_KEY: ${{ secrets.ZHIPUAI_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | SHA=${{ github.event.pull_request.head.sha || github.sha }} - echo "Starting review with GLM-5 for SHA $SHA..." - - $HOME/.opencode/bin/opencode run --model zai-coding-plan/glm-5 "Review the changes in this PR for the Hush Semantic Gateway. + PR_NUMBER=${{ github.event.pull_request.number }} + echo "Starting review with hush-zhipu/glm-5 for PR #$PR_NUMBER SHA $SHA..." + echo "opencode.json:"; cat opencode.json + echo "Hush health:"; curl -sf http://127.0.0.1:4000/health || echo "gateway unreachable" + + $HOME/.opencode/bin/opencode run --model hush-zhipu/glm-5 "Review the changes in PR #$PR_NUMBER for the Hush Semantic Gateway. + + **IMPORTANT**: This is a code review only. Do NOT run tests, npm commands, or build commands. Only read source files and git diffs. Focus areas: 1. **Redaction Logic**: Ensure PII patterns are robust and handle edge cases in tool outputs (like JSON or CLI tables). 2. **Streaming Integrity**: Check that the SSE/streaming proxy logic doesn't buffer unnecessarily or break the rehydration flow. 3. **Security**: Look for potential PII leaks or insecure token handling in the vault. 4. **Reliability**: Ensure the proxy handles upstream errors gracefully. - - Keep the summary concise but technical. Post findings as a markdown comment on the PR. - + + Keep the review concise. Post findings as a single markdown comment using: + gh pr comment $PR_NUMBER --body \"\" + + Do NOT try to auto-detect the PR number — use exactly $PR_NUMBER. + **CRITICAL**: Include the string 'Reviewed SHA: $SHA' at the very end of your comment so I can track which commits have been reviewed." diff --git a/examples/team-config/opencode.json b/examples/team-config/opencode.json index 059a077..27baff8 100644 --- a/examples/team-config/opencode.json +++ b/examples/team-config/opencode.json @@ -1,8 +1,15 @@ { "provider": { - "zai-coding-plan": { + "hush-zhipu": { + "npm": "@ai-sdk/openai-compatible", + "name": "Hush ZhipuAI Proxy", "options": { - "baseURL": "http://127.0.0.1:4000/api/coding/paas/v4" + "baseURL": "http://127.0.0.1:4000/api/coding/paas/v4", + "apiKey": "{env:ZHIPU_API_KEY}" + }, + "models": { + "glm-5": { "name": "GLM 5" }, + "glm-4.7-flash": { "name": "GLM 4.7 Flash" } } } }, diff --git a/package-lock.json b/package-lock.json index 66b2e35..7db1326 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@aictrl/hush", - "version": "0.1.6", + "version": "0.1.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@aictrl/hush", - "version": "0.1.6", + "version": "0.1.7", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.27.1", @@ -4197,9 +4197,9 @@ } }, "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "license": "MIT", "dependencies": { "sax": ">=0.6.0", diff --git a/package.json b/package.json index e5a2d73..166353d 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,9 @@ "pino": "^10.3.1", "pino-pretty": "^13.1.3" }, + "overrides": { + "xml2js": "^0.6.2" + }, "devDependencies": { "@types/blessed": "^0.1.27", "@types/cors": "^2.8.19", diff --git a/src/index.ts b/src/index.ts index 0f4cd24..297a6e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -105,7 +105,7 @@ async function proxyRequest( method: req.method, headers: fetchHeaders, body: hasBody ? JSON.stringify(redactedBody) : undefined, - signal: AbortSignal.timeout(30000), // 30s timeout + signal: AbortSignal.timeout(120000), // 120s timeout (LLM first-token can be slow) }); // Handle Upstream Errors (4xx, 5xx)