Skip to content

Commit

Permalink
feat: add npm publish workflow (#29)
Browse files Browse the repository at this point in the history
* feat: add npm publish workflow

Co-Authored-By: Han Xiao <[email protected]>

* feat: add CLI interface

Co-Authored-By: Han Xiao <[email protected]>

* fix: add moduleResolution and resolveJsonModule to tsconfig

Co-Authored-By: Han Xiao <[email protected]>

* feat: add OPENAI_API_KEY to workflow files

Co-Authored-By: Han Xiao <[email protected]>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Han Xiao <[email protected]>
  • Loading branch information
devin-ai-integration[bot] and hanxiao authored Feb 6, 2025
1 parent aee5598 commit 282de3f
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 3 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: NPM Publish

on:
release:
types: [created]

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run lint and tests
env:
BRAVE_API_KEY: ${{ secrets.BRAVE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
JINA_API_KEY: ${{ secrets.JINA_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
npm run lint
npm test
- name: Build TypeScript
run: npm run build

- name: Publish to npm
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ jobs:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
JINA_API_KEY: ${{ secrets.JINA_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm test
21 changes: 21 additions & 0 deletions package-lock.json

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

11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
{
"name": "node-deepresearch",
"version": "1.0.0",
"main": "index.js",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"README.md",
"LICENSE"
],
"scripts": {
"prepare": "npm run build",
"build": "tsc",
"dev": "npx ts-node src/agent.ts",
"search": "npx ts-node src/test-duck.ts",
Expand All @@ -25,6 +32,7 @@
"@types/node-fetch": "^2.6.12",
"ai": "^4.1.21",
"axios": "^1.7.9",
"commander": "^13.1.0",
"cors": "^2.8.5",
"duck-duck-scrape": "^2.2.7",
"express": "^4.21.2",
Expand All @@ -33,6 +41,7 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@types/commander": "^2.12.0",
"@types/jest": "^29.5.14",
"@types/node": "^22.10.10",
"@typescript-eslint/eslint-plugin": "^7.0.1",
Expand Down
40 changes: 40 additions & 0 deletions src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

// Mock environment variables
process.env.GEMINI_API_KEY = 'test-key';
process.env.JINA_API_KEY = 'test-key';

jest.mock('../agent', () => ({
getResponse: jest.fn().mockResolvedValue({
result: {
action: 'answer',
answer: 'Test answer',
references: []
}
})
}));

describe('CLI', () => {
test('shows version', async () => {
const { stdout } = await execAsync('ts-node src/cli.ts --version');
expect(stdout.trim()).toMatch(/\d+\.\d+\.\d+/);
});

test('shows help', async () => {
const { stdout } = await execAsync('ts-node src/cli.ts --help');
expect(stdout).toContain('deepresearch');
expect(stdout).toContain('AI-powered research assistant');
});

test('handles invalid token budget', async () => {
try {
await execAsync('ts-node src/cli.ts -t invalid "test query"');
fail('Should have thrown');
} catch (error) {
expect((error as { stderr: string }).stderr).toContain('Invalid token budget: must be a number');
}
});
});
48 changes: 48 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env node
import { Command } from 'commander';
import { getResponse } from './agent';
import { version } from '../package.json';

const program = new Command();

program
.name('deepresearch')
.description('AI-powered research assistant that keeps searching until it finds the answer')
.version(version)
.argument('<query>', 'The research query to investigate')
.option('-t, --token-budget <number>', 'Maximum token budget', (val) => {
const num = parseInt(val);
if (isNaN(num)) throw new Error('Invalid token budget: must be a number');
return num;
}, 1000000)
.option('-m, --max-attempts <number>', 'Maximum bad attempts before giving up', (val) => {
const num = parseInt(val);
if (isNaN(num)) throw new Error('Invalid max attempts: must be a number');
return num;
}, 3)
.option('-v, --verbose', 'Show detailed progress')
.action(async (query: string, options: any) => {
try {
const { result } = await getResponse(
query,
parseInt(options.tokenBudget),
parseInt(options.maxAttempts)
);

if (result.action === 'answer') {
console.log('\nAnswer:', result.answer);
if (result.references?.length) {
console.log('\nReferences:');
result.references.forEach(ref => {
console.log(`- ${ref.url}`);
console.log(` "${ref.exactQuote}"`);
});
}
}
} catch (error) {
console.error('Error:', error instanceof Error ? error.message : String(error));
process.exit(1);
}
});

program.parse();
6 changes: 4 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"strict": true
"strict": true,
"resolveJsonModule": true,
"moduleResolution": "node"
}
}
}

0 comments on commit 282de3f

Please sign in to comment.