Skip to content

Commit 0377ff1

Browse files
Add Jina API integration with fetch utility functions for page reading and searching
1 parent f24caf5 commit 0377ff1

9 files changed

Lines changed: 282 additions & 73 deletions

File tree

src/commands/settings.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {parseArgs} from "jsr:@std/cli/parse-args"
22
import {loadUserSettings, saveUserSettings} from "../config/settings.ts"
3-
import {parseFlagForHelp} from "../utils/functions.ts"
3+
import {parseFlagForHelp, getCommandHelp} from "../utils/functions.ts"
44

55
export async function handleSettings(args: string[]): Promise<void> {
66
const settingsFlags = {
7-
string: ["set-name", "add-command", "remove-command"],
7+
string: ["set-name", "add-command", "remove-command", "set-jina-key"],
88
boolean: ["list"],
99
}
1010
const flags = parseArgs(args, settingsFlags)
@@ -18,18 +18,42 @@ export async function handleSettings(args: string[]): Promise<void> {
1818
if (flags["set-name"]) {
1919
settings.userName = flags["set-name"]
2020
}
21+
else if (flags["set-jina-key"]) {
22+
settings.jinaApiKey = flags["set-jina-key"]
23+
console.log("Jina API key has been set")
24+
}
2125
else if (flags["add-command"]) {
22-
const addCommandValue = flags["add-command"] as string;
23-
const [name, description, helpCmd, ...helpFlags] = addCommandValue.split(",")
24-
settings.customCommands.push({
25-
name,
26-
description,
27-
helpCommand: helpCmd,
28-
helpFlags: helpFlags
29-
})
26+
const commandNameFromArg = flags["add-command"] as string
27+
let commandName = commandNameFromArg
28+
let commandDescription = ""
29+
if (commandNameFromArg.includes(",")) {
30+
const commandSplit = commandNameFromArg.split(",")
31+
commandName = commandSplit[0]
32+
commandDescription = commandSplit[1]
33+
}
34+
const helpInfo = await getCommandHelp(commandName)
35+
console.log(`Got help info for ${commandName}}`)
36+
37+
const existingCommandIndex = settings.customCommands.findIndex(cmd => cmd.name === commandName)
38+
if (existingCommandIndex >= 0) {
39+
settings.customCommands[existingCommandIndex] = {
40+
name: commandName,
41+
description: commandDescription || helpInfo.description,
42+
helpText: helpInfo.helpText,
43+
}
44+
} else {
45+
settings.customCommands.push({
46+
name: commandName,
47+
description: commandDescription || helpInfo.description,
48+
helpText: helpInfo.helpText,
49+
})
50+
}
51+
console.log(`Added command ${commandName} with help information`)
3052
}
3153
else if (flags["remove-command"]) {
32-
const index = settings.customCommands.findIndex(cmd => cmd.name === flags["remove-command"])
54+
const commandNameFromArg = flags["remove-command"] as string
55+
const index = settings.customCommands.findIndex(cmd => cmd.name === commandNameFromArg)
56+
console.log(`Removing command ${flags["remove-command"]} at index ${index}`)
3357
if (index >= 0) settings.customCommands.splice(index, 1)
3458
}
3559
else if (flags.list) {

src/config/constants.ts

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {join} from "jsr:@std/path"
22
import Anthropic from "npm:@anthropic-ai/sdk"
33
import {homedir} from "node:os"
4-
import {loadUserSettings} from "./settings.ts"
4+
import {isJinaAvailable, loadUserSettings} from "./settings.ts"
55

66
export const EDITOR_DIR = join(homedir(), ".ComputerUseAgent", "editor_dir")
77
export const SESSIONS_DIR = join(homedir(), ".ComputerUseAgent", "sessions")
@@ -63,6 +63,15 @@ You have access to following tools and capabilities:
6363
- CLIPBOARD_TOOLS:
6464
- Name: "read_clipboard"
6565
- Arguments: none
66+
${isJinaAvailable() ? `
67+
- JINA_TOOLS:
68+
- Name: "readPage"
69+
- Arguments: {url: string}
70+
- Name: "search"
71+
- Arguments: {searchTerm: string}
72+
- Name: "searchGrounding"
73+
- Arguments: {searchTerm: string}
74+
` : ''}
6675
6776
Before taking any action, follow these steps:
6877
@@ -93,6 +102,15 @@ Your capabilities include:
93102
- Can add, retrieve, and clear memories
94103
- Use memory for context persistence
95104
105+
4. Clipboard Access:
106+
- Can read content from the system clipboard
107+
${isJinaAvailable() ? `
108+
5. Jina API Integration:
109+
- Can read and parse content from a URL
110+
- Can search content using Jina Search API
111+
- Can search with grounding using Jina Grounding API
112+
` : ''}
113+
96114
Always present your solution in this order:
97115
1. Understanding of the request
98116
2. Step-by-step plan
@@ -101,7 +119,7 @@ Always present your solution in this order:
101119
export const API_CONFIG = {
102120
MODEL: "claude-3-5-sonnet-20241022",
103121
INTENT_MODEL: "claude-3-5-haiku-20241022",
104-
MAX_TOKENS: 4096,
122+
MAX_TOKENS: 8192,
105123
MAX_INTENT_TOKENS: 20,
106124
}
107125

@@ -150,12 +168,66 @@ export const CLIPBOARD_TOOLS: Anthropic.Beta.BetaTool[] = [
150168
}
151169
]
152170

171+
export const JINA_TOOLS: Anthropic.Beta.BetaTool[] = [
172+
{
173+
type: "custom",
174+
name: "readPage",
175+
description: "Read and parse content from a URL using Jina Reader API",
176+
input_schema: {
177+
type: "object",
178+
properties: {
179+
url: {
180+
type: "string",
181+
description: "The URL to read and parse",
182+
},
183+
},
184+
required: ["url"],
185+
},
186+
},
187+
{
188+
type: "custom",
189+
name: "search",
190+
description: "Search content using Jina Search API",
191+
input_schema: {
192+
type: "object",
193+
properties: {
194+
searchTerm: {
195+
type: "string",
196+
description: "The term to search for",
197+
},
198+
},
199+
required: ["searchTerm"],
200+
},
201+
},
202+
{
203+
type: "custom",
204+
name: "searchGrounding",
205+
description: "Search with grounding using Jina Grounding API",
206+
input_schema: {
207+
type: "object",
208+
properties: {
209+
searchTerm: {
210+
type: "string",
211+
description: "The term to search for with grounding",
212+
},
213+
},
214+
required: ["searchTerm"],
215+
},
216+
},
217+
]
218+
219+
export const ALL_TOOLS = [
220+
...MEMORY_TOOLS,
221+
...CLIPBOARD_TOOLS,
222+
...(isJinaAvailable() ? JINA_TOOLS : []),
223+
]
224+
153225
export function getSystemContext(basePrompt: string): string {
154226
const settings = loadUserSettings()
155227
const customCommandsContext = settings.customCommands.length > 0
156-
? "\nCustom Commands:\n" + settings.customCommands
228+
? "\nAvailable Commands:\n" + settings.customCommands
157229
.map(cmd => `- ${cmd.name}: ${cmd.description}${cmd.helpCommand ? `\n Help: ${cmd.helpCommand}` : ''
158-
}${cmd.helpFlags ? `\n Flags: ${cmd.helpFlags.join(', ')}` : ''
230+
}${cmd.helpText ? `\n Flags: ${cmd.helpText}` : ''
159231
}`).join('\n')
160232
: ''
161233

src/config/settings.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {UserSettings} from "../types/interfaces.ts"
44

55
const DEFAULT_SETTINGS: UserSettings = {
66
userName: "User",
7-
customCommands: []
7+
customCommands: [],
8+
jinaApiKey: undefined
89
}
910

1011
const SETTINGS_PATH = join(homedir(), ".ComputerUseAgent", "settings.json")
@@ -21,3 +22,13 @@ export function loadUserSettings(): UserSettings {
2122
export function saveUserSettings(settings: UserSettings) {
2223
Deno.writeTextFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2))
2324
}
25+
26+
export function isJinaAvailable(): boolean {
27+
const settings = loadUserSettings();
28+
return Boolean(settings.jinaApiKey);
29+
}
30+
31+
export function getJinaApiKey(): string {
32+
const settings = loadUserSettings();
33+
return settings.jinaApiKey || "";
34+
}

src/modules/bash/handlers.ts

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,39 +32,60 @@ export class BashHandlers {
3232
return {content: "in mock mode, command did not run"}
3333
}
3434

35-
log.info(`Executing bash command: ${command}`)
3635

3736
const shell = Deno.env.get("SHELL") || "bash"
38-
const process = new Deno.Command(shell, {
39-
args: ["-c", command],
40-
env: this.environment,
41-
stdout: "piped",
42-
stderr: "piped",
43-
})
44-
const {stdout, stderr, code} = await process.output()
37+
log.info(`Executing command: ${command}`)
4538

46-
const output = new TextDecoder().decode(stdout).trim()
47-
const errorOutput = new TextDecoder().decode(stderr).trim()
39+
try {
40+
// First attempt: Try running the command directly
41+
const directProcess = new Deno.Command(command.split(" ")[0], {
42+
args: command.split(" ").slice(1),
43+
env: this.environment,
44+
stdout: "piped",
45+
stderr: "piped",
46+
})
47+
const result = await directProcess.output()
4848

49-
if (code === 0) {
50-
if (output) {
51-
log.info(
52-
`Command output:\n\n\`\`\`output for '${command.slice(0, 20)
53-
}...'\n${output}\n\`\`\``,
54-
)
49+
const output = new TextDecoder().decode(result.stdout).trim()
50+
if (result.code === 0) {
51+
if (output) {
52+
log.info(`Command output:\n\n\`\`\`output for '${command.slice(0, 20)}...'\n${output}\n\`\`\``)
53+
}
54+
return {content: output}
55+
} else {
56+
const errorMessage = new TextDecoder().decode(result.stderr).trim()
57+
log.error({errorMessage})
58+
throw new Error(errorMessage)
5559
}
56-
} else if (errorOutput) {
57-
log.error(
58-
`Command error output:\n\n\`\`\`error for '${command}'\n${errorOutput}\n\`\`\``,
59-
)
60-
}
60+
} catch (error) {
61+
log.info(`Direct execution failed, trying with shell: ${shell} -c ${command}`)
6162

62-
if (code !== 0) {
63-
const errorMessage = errorOutput || "Command execution failed."
64-
return {error: errorMessage}
65-
}
63+
try {
64+
// Second attempt: Try with shell -c
65+
const shellProcess = new Deno.Command(shell, {
66+
args: ["-c", command],
67+
env: this.environment,
68+
stdout: "piped",
69+
stderr: "piped",
70+
})
71+
const {stdout, stderr, code} = await shellProcess.output()
72+
73+
const output = new TextDecoder().decode(stdout).trim()
74+
const errorOutput = new TextDecoder().decode(stderr).trim()
6675

67-
return {content: output}
76+
if (code === 0) {
77+
if (output) {
78+
log.info(`Command output:\n\n\`\`\`output for '${command.slice(0, 20)}...'\n${output}\n\`\`\``)
79+
}
80+
return {content: output}
81+
} else {
82+
log.error(`Command error output:\n\n\`\`\`error for '${command}'\n${errorOutput}\n\`\`\``)
83+
return {error: errorOutput || "Command execution failed."}
84+
}
85+
} catch (finalError) {
86+
return {error: String(finalError)}
87+
}
88+
}
6889
} catch (error) {
6990
log.error(`Error in handleBashCommand: ${error}`)
7091
return {error: String(error)}

src/modules/hybrid/hybrid_session.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {BaseSession} from "../../utils/session.ts"
2-
import {COMBINED_SYSTEM_PROMPT, API_CONFIG, MEMORY_TOOLS, CLIPBOARD_TOOLS} from "../../config/constants.ts"
2+
import {COMBINED_SYSTEM_PROMPT, API_CONFIG, MEMORY_TOOLS, CLIPBOARD_TOOLS, JINA_TOOLS} from "../../config/constants.ts"
33
import {log} from "../../config/logging.ts"
44
import {ToolHandler} from "../../utils/tool_handler.ts"
55

@@ -41,6 +41,7 @@ export class HybridSession extends BaseSession {
4141
{type: "text_editor_20241022", name: "str_replace_editor"},
4242
...MEMORY_TOOLS,
4343
...CLIPBOARD_TOOLS,
44+
...JINA_TOOLS
4445
],
4546
system: this.getSystemPrompt(`${COMBINED_SYSTEM_PROMPT}\nSystem Context: ${JSON.stringify(systemInfo)}`),
4647
betas: ["computer-use-2024-10-22"],

src/types/interfaces.ts

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,51 @@
11
export interface ToolCall {
2-
type: string;
3-
name: string;
4-
id: string;
2+
type: string
3+
name: string
4+
id: string
55
input: {
6-
command: string;
7-
path: string;
8-
file_text?: string;
9-
old_str?: string;
10-
new_str?: string;
11-
insert_line?: number;
12-
};
6+
command: string
7+
path: string
8+
file_text?: string
9+
old_str?: string
10+
new_str?: string
11+
insert_line?: number
12+
}
1313
}
1414

1515
export interface ToolResult {
16-
tool_call_id: string;
16+
tool_call_id: string
1717
output: {
18-
type: string;
19-
content: Array<{ type: string; text: string }>;
20-
tool_use_id: string;
21-
is_error: boolean;
22-
};
18+
type: string
19+
content: Array<{type: string; text: string}>
20+
tool_use_id: string
21+
is_error: boolean
22+
}
2323
}
2424

2525
export interface SystemInfo {
26-
os: string;
27-
arch: string;
28-
isWsl: boolean;
29-
shell: string;
26+
os: string
27+
arch: string
28+
isWsl: boolean
29+
shell: string
3030
}
3131

3232
export interface Memory {
33-
id: string;
34-
content: string;
35-
timestamp: number;
33+
id: string
34+
content: string
35+
timestamp: number
3636
}
3737

3838
export interface MemoryFile {
39-
memories: Memory[];
39+
memories: Memory[]
4040
}
4141

4242
export interface UserSettings {
43-
userName: string;
43+
userName: string
4444
customCommands: {
45-
name: string;
46-
description: string;
47-
helpCommand?: string;
48-
helpFlags?: string[];
49-
}[];
45+
name: string
46+
description: string
47+
helpCommand?: string
48+
helpText?: string
49+
}[]
50+
jinaApiKey?: string
5051
}

0 commit comments

Comments
 (0)