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
18 changes: 9 additions & 9 deletions .github/workflows/test-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ name: Test Streetrace Action
on:
workflow_dispatch:
inputs:
agentDefinition:
description: 'Agent definition name to test (e.g., myagent@v1)'
agentName:
description: 'Agent name to test (e.g., myagent@v1)'
required: false
default: 'testagent@v1'
apiUrl:
Expand All @@ -26,7 +26,7 @@ jobs:
uses: ./ # Uses the action from the current repository
with:
claims: |
agent_definition: "${{ github.event.inputs.agentDefinition }}"
agent_name: "${{ github.event.inputs.agentName }}"
env:
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY || 'test-api-key' }}
STREETRACE_API_URL: ${{ github.event.inputs.apiUrl }}
Expand Down Expand Up @@ -66,13 +66,13 @@ jobs:
- name: ls -la
run: ls -la

- name: Check agent definition
- name: Check agent
run: |
if [ -f agents/${{ github.event.inputs.agentDefinition }}.yaml ]; then
echo "## Agent definition downloaded:"
cat agents/${{ github.event.inputs.agentDefinition }}.yaml
if [ -f agents/${{ github.event.inputs.agentName }}.yaml ]; then
echo "## Agent downloaded:"
cat agents/${{ github.event.inputs.agentName }}.yaml
else
echo "Agent definition file not found"
echo "Agent file not found"
fi

test-action-in-pr:
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
uses: ./
with:
claims: |
agent_definition: "${{ github.event.inputs.agentDefinition || 'testagent@v1' }}"
agent_name: "${{ github.event.inputs.agentName || 'testagent@v1' }}"
env:
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY || 'test-api-key' }}
STREETRACE_API_URL: ${{ github.event.inputs.apiUrl || 'https://api.streetrace.run/' }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ jobs:
uses: ./
with:
claims: |
agent_definition: "repositoryanalyzer@v1"
agent_name: "repositoryanalyzer@v1"
env:
# Using test values - the action should fallback to local agent definition
# Using test values - the action should fallback to local agent
STREETRACE_API_KEY: 'test-api-key-for-ci'
STREETRACE_API_URL: 'https://api.streetrace.run/'
continue-on-error: true # Don't fail the workflow if action fails
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Run Streetrace agents in your CI/CD pipeline to automate code reviews, testing,

## Features

- 🚀 Download and execute Streetrace agent definitions
- 🚀 Download and execute Streetrace agents
- 💬 Automatic PR comments with agent output
- 🔧 Easy integration with existing workflows
- 🔒 Secure API key handling
Expand All @@ -27,7 +27,7 @@ jobs:
uses: streetrace-ai/github-action@v1
with:
claims: |
agent_definition: "myagent@v2"
agent_name: "myagent@v2"
recipe: "code_review@v1"
env:
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY }}
Expand All @@ -36,8 +36,8 @@ jobs:

### Inputs

| Input | Description | Required | Default |
| -------- | ------------------------------------------------ | -------- | ------- |
| Input | Description | Required | Default |
| -------- | ----------------------------------------------- | -------- | ------- |
| `claims` | YAML key-value pairs for configuring the agent | Yes | - |
| `prompt` | Optional prompt to pass to the Streetrace agent | No | - |

Expand All @@ -60,9 +60,9 @@ You can pass any additional environment variables (like repository secrets) to y

Currently supported claims:

| Claim | Description | Required |
| ------------------ | -------------------------------------------------- | -------- |
| `agent_definition` | Name and version of the agent (e.g., `myagent@v2`) | Yes |
| Claim | Description | Required |
| ------------ | -------------------------------------------------- | -------- |
| `agent_name` | Name and version of the agent (e.g., `myagent@v2`) | Yes |

### Outputs

Expand All @@ -88,7 +88,7 @@ permissions:
- uses: streetrace-ai/github-action@v1
with:
claims: |
agent_definition: "codereview@v1"
agent_name: "codereview@v1"
env:
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY }}
STREETRACE_API_URL: 'https://custom.streetrace.example.com/'
Expand All @@ -101,7 +101,7 @@ permissions:
id: streetrace
with:
claims: |
agent_definition: "analyzer@v2"
agent_name: "analyzer@v2"
env:
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY }}

Expand All @@ -117,7 +117,7 @@ permissions:
- uses: streetrace-ai/github-action@v1
with:
claims: |
agent_definition: "security_scanner@v3"
agent_name: "security_scanner@v3"
recipe: "vulnerability_scan@v1"
severity_threshold: "high"
env:
Expand All @@ -132,7 +132,7 @@ Pass repository secrets or other environment variables to the Streetrace agent u
- uses: streetrace-ai/github-action@v1
with:
claims: |
agent_definition: "deployment@v1"
agent_name: "deployment@v1"
env:
# Required Streetrace variables
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY }}
Expand All @@ -150,8 +150,8 @@ Override the default prompt:
- uses: streetrace-ai/github-action@v1
with:
claims: |
agent_definition: "analyzer@v2"
prompt: "Focus on security vulnerabilities in the authentication module"
agent_name: "analyzer@v2"
prompt: 'Focus on security vulnerabilities in the authentication module'
env:
STREETRACE_API_KEY: ${{ secrets.STREETRACE_API_KEY }}
```
Expand All @@ -160,7 +160,7 @@ Override the default prompt:

1. Get your Streetrace API key from [streetrace.run](https://streetrace.run/)
2. Add it as a secret in your GitHub repository (Settings → Secrets → Actions)
3. Create your agent definition in the Streetrace platform
3. Create your agent in the Streetrace platform
4. Add this action to your workflow

## Requirements
Expand Down
66 changes: 34 additions & 32 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34039,14 +34039,14 @@ const { URL } = __nccwpck_require__(7016);
const core = __nccwpck_require__(6966);

/**
* Downloads the agent definition from Streetrace API
* Downloads the agent from Streetrace API
* @param {string} apiUrl - The base URL of the Streetrace API
* @param {string} apiKey - The API key for authentication
* @param {string} agentDefinitionName - The name of the agent definition
* @returns {Promise<string>} The agent definition YAML content
* @param {string} agentName - The name of the agent
* @returns {Promise<string>} The agent YAML content
*/
async function downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName) {
const url = new URL(`/api/agentdefs/${agentDefinitionName}`, apiUrl);
async function downloadAgent(apiUrl, apiKey, agentName) {
const url = new URL(`/api/agents/${agentName}`, apiUrl);

return await new Promise((resolve, reject) => {
const protocol = url.protocol === 'https:' ? https : http;
Expand Down Expand Up @@ -34098,11 +34098,9 @@ async function downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName) {
core.error(`Authentication failed with API key: ${maskedKey}`);
reject(new Error('Unauthorized: Invalid API key'));
} else if (res.statusCode === 404) {
reject(new Error(`Agent definition '${agentDefinitionName}' not found`));
reject(new Error(`Agent '${agentName}' not found`));
} else {
reject(
new Error(`Failed to download agent definition: HTTP ${res.statusCode} - ${data}`)
);
reject(new Error(`Failed to download agent: HTTP ${res.statusCode} - ${data}`));
}
}
});
Expand All @@ -34120,7 +34118,7 @@ async function downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName) {
}

module.exports = {
downloadAgentDefinition,
downloadAgent,
};


Expand Down Expand Up @@ -36045,7 +36043,7 @@ const io = __nccwpck_require__(378);
const fs = (__nccwpck_require__(9896).promises);
const path = __nccwpck_require__(6928);
const yaml = __nccwpck_require__(4852);
const { downloadAgentDefinition } = __nccwpck_require__(8166);
const { downloadAgent } = __nccwpck_require__(8166);

async function run() {
try {
Expand All @@ -36070,29 +36068,29 @@ async function run() {
throw new Error(`Failed to parse claims YAML: ${error.message}`);
}

// Extract agent_definition from claims
const agentDefinitionName = claims.agent_definition;
if (!agentDefinitionName) {
throw new Error('claims must include an "agent_definition" key');
// Extract agent from claims
const agentName = claims.agent_name;
if (!agentName) {
throw new Error('claims must include an "agent" key');
}

// Validate agentDefinitionName is alphanumeric (supports @ and version suffixes like abc@v2)
if (!/^[a-zA-Z0-9@_.-]+$/.test(agentDefinitionName)) {
throw new Error('agent_definition must be alphanumeric with optional @, _, ., - characters');
// Validate agentName is alphanumeric (supports @ and version suffixes like abc@v2)
if (!/^[a-zA-Z0-9@_.-]+$/.test(agentName)) {
throw new Error('agent must be alphanumeric with optional @, _, ., - characters');
}

core.info(`Starting Streetrace action with agent: ${agentDefinitionName}`);
core.info(`Starting Streetrace action with agent: ${agentName}`);

// Step 1: Download agent definition
core.info('Downloading agent definition...');
const agentDefinition = await downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName);
// Step 1: Download agent
core.info('Downloading agent...');
const agent = await downloadAgent(apiUrl, apiKey, agentName);

// Step 2: Save the definition to file
const agentsDir = './agents';
await io.mkdirP(agentsDir);
const agentFilePath = path.join(agentsDir, `${agentDefinitionName}.yaml`);
await fs.writeFile(agentFilePath, agentDefinition);
core.info(`Agent definition saved to ${agentFilePath}`);
const agentFilePath = path.join(agentsDir, `${agentName}.yaml`);
await fs.writeFile(agentFilePath, agent);
core.info(`Agent saved to ${agentFilePath}`);

// Step 3: Install streetrace via pipx
core.info('Installing streetrace...');
Expand All @@ -36112,7 +36110,11 @@ async function run() {
// - Specific branch: git+https://github.com/streetrace-ai/streetrace.git@branch-name
// - Specific tag: git+https://github.com/streetrace-ai/streetrace.git@v1.0.0
// - Specific commit: git+https://github.com/streetrace-ai/streetrace.git@abc123
await exec.exec('pipx', ['install', 'git+https://github.com/streetrace-ai/streetrace.git', '--force']);
await exec.exec('pipx', [
'install',
'git+https://github.com/streetrace-ai/streetrace.git',
'--force',
]);

// Step 4: Run streetrace ci command
core.info('Running streetrace agent...');
Expand All @@ -36139,7 +36141,7 @@ async function run() {

// Determine which prompt to use based on priority:
// 1. Action input prompt (highest priority)
// 2. Agent definition YAML prompt
// 2. Agent YAML prompt
// 3. No prompt (run without --prompt flag)

let promptToUse = null;
Expand All @@ -36149,15 +36151,15 @@ async function run() {
promptToUse = promptInput;
core.info(`Using prompt from action input`);
} else {
// Priority 2: Check if agent definition YAML contains a prompt
// Priority 2: Check if agent YAML contains a prompt
try {
const agentYaml = yaml.load(agentDefinition);
const agentYaml = yaml.load(agent);
if (agentYaml && agentYaml.prompt) {
promptToUse = agentYaml.prompt;
core.info(`Using prompt from agent definition YAML`);
core.info(`Using prompt from agent YAML`);
}
} catch (error) {
core.warning(`Could not parse agent definition YAML for prompt: ${error.message}`);
core.warning(`Could not parse agent YAML for prompt: ${error.message}`);
}
}

Expand All @@ -36180,7 +36182,7 @@ async function run() {
outputContent = streetraceOutput;
}
if (outputContent) {
outputContent += "\n\n---\n*Generated by [Streetrace](https://streetrace.run/)*"
outputContent += '\n\n---\n*Generated by [Streetrace](https://streetrace.run/)*';
}

// Step 6: Set output
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

18 changes: 8 additions & 10 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ const { URL } = require('url');
const core = require('@actions/core');

/**
* Downloads the agent definition from Streetrace API
* Downloads the agent from Streetrace API
* @param {string} apiUrl - The base URL of the Streetrace API
* @param {string} apiKey - The API key for authentication
* @param {string} agentDefinitionName - The name of the agent definition
* @returns {Promise<string>} The agent definition YAML content
* @param {string} agentName - The name of the agent
* @returns {Promise<string>} The agent YAML content
*/
async function downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName) {
const url = new URL(`/api/agentdefs/${agentDefinitionName}`, apiUrl);
async function downloadAgent(apiUrl, apiKey, agentName) {
const url = new URL(`/api/agents/${agentName}`, apiUrl);

return await new Promise((resolve, reject) => {
const protocol = url.protocol === 'https:' ? https : http;
Expand Down Expand Up @@ -63,11 +63,9 @@ async function downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName) {
core.error(`Authentication failed with API key: ${maskedKey}`);
reject(new Error('Unauthorized: Invalid API key'));
} else if (res.statusCode === 404) {
reject(new Error(`Agent definition '${agentDefinitionName}' not found`));
reject(new Error(`Agent '${agentName}' not found`));
} else {
reject(
new Error(`Failed to download agent definition: HTTP ${res.statusCode} - ${data}`)
);
reject(new Error(`Failed to download agent: HTTP ${res.statusCode} - ${data}`));
}
}
});
Expand All @@ -85,5 +83,5 @@ async function downloadAgentDefinition(apiUrl, apiKey, agentDefinitionName) {
}

module.exports = {
downloadAgentDefinition,
downloadAgent,
};
Loading
Loading