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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ This will start an interactive chat session.

## Current status

This is in active development. Don't expect much yet.
This is in frenetic development. Don't expect too much yet, nor API stability.

## Releasing

From the latest version of main, run `npm run release`. This will select a version bump based on semantic commits, create a release branch, edit the changelog, and push. (You may want to optionally edit the changelog for clarity).

Publish this branch, and merge with main. This will trigger the build & publish to NPM.
Publish this branch, and merge with main. This will trigger the build & publish to NPM.
3 changes: 2 additions & 1 deletion cli/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
OPENAI_API_KEY=""
OPENAI_API_KEY=""
SERP_API_KEY=""
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Box, Text } from 'ink';
import { CliMessage } from '../shellMessages.js';
import { CliMessage } from './shell-messages.js';

// Memoize the component to prevent unnecessary re-renders
export const Message = React.memo(({ msg }: { msg: CliMessage }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { spawn } from 'child_process';
import { ulid } from 'ulid';
import { ShellMessage } from '../shellMessages.js';
import { ShellMessage } from '../shell-messages.js';
import { homedir } from 'os';
import { resolve } from 'path';

Expand Down Expand Up @@ -70,8 +70,6 @@ export class ShellExecutor {
'cat',
'grep',
'find',
'ps',
'top',
'git',
'node',
'npm',
Expand Down
File renamed without changes.
137 changes: 137 additions & 0 deletions cli/deprecated/useKernel.bu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useState, useEffect, useRef } from 'react';
import { openai } from '@ai-sdk/openai';
import {
KernelMessage,
createMessage,
InputMessage,
ToolResultsMessage,
} from '@unternet/kernel';
import { Interpreter } from '@unternet/kernel';
import { tools } from '../src/tools.js';
import { ShellExecutor } from '../src/services/ShellExecutor.js';
import { CliMessage } from '../src/shell-messages.js';

/**
* Hook for managing kernel interactions and message state
*/
export function useKernel() {
// Store all messages for display - but we could optimize this later
const [messages, setMessages] = useState<CliMessage[]>([]);
const [currentDirectory, setCurrentDirectory] = useState<string>(
ShellExecutor.getShortWorkingDirectory()
);

// Initialize interpreter with tools
const interpreterRef = useRef<Interpreter | null>(null);
if (!interpreterRef.current) {
const model = openai('gpt-4o');
interpreterRef.current = new Interpreter({
model,
tools,
prompts: {
system: () =>
`You are an intelligent computer operating system, operating in a shell environment. If a shell command is executed, its output will be shown directly to the user, so you don't need to respond further. Use the stop signal tool to stop output.`,
},
});
}
const interpreter = interpreterRef.current;

const addMessage = (message: CliMessage) => {
setMessages((prev) => [message, ...prev]);
};

const updateReplyMessage = (response: any) => {
setMessages((prev) => {
const messageId = response.id;
const deltaText = response.delta.text || '';

// Find existing message or create new one
const existingIndex = prev.findIndex(
(msg) => msg.id === messageId && msg.type === 'reply'
);

if (existingIndex >= 0) {
// Update existing message
const updated = [...prev];
const existingMsg = updated[existingIndex] as any;
updated[existingIndex] = {
...existingMsg,
text: existingMsg.text + deltaText,
};
return updated;
} else {
// Create new reply message
const newReply = {
id: messageId,
type: 'reply' as const,
text: deltaText,
timestamp: Date.now(),
} as KernelMessage;
return [newReply, ...prev];
}
});
};

const executeTools = async (toolCalls: any) => {
const resultsMsg = createMessage<ToolResultsMessage>({
type: 'tool_results',
results: [],
});

for (const call of toolCalls.calls) {
const { id, name, args } = call;
const tool = tools.find((t) => t.name === name);

if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}

// Execute the tool and add result
resultsMsg.results.push({
callId: id,
name: name,
output: await tool.execute!(args),
});

// Update current directory if this was a shell command (cd could have changed it)
if (name === 'shell_command') {
setCurrentDirectory(ShellExecutor.getShortWorkingDirectory());
}
}

// Always send the tool results message
addMessage(resultsMsg);
interpreter.send(resultsMsg);
};

useEffect(() => {
const handleResponse = (response: any) => {
if (response.type === 'reply.delta') {
updateReplyMessage(response);
} else if (response.type === 'tool_calls') {
addMessage(response);
executeTools(response);
}
};

interpreter.on('response', handleResponse);
}, [interpreter]);

const sendMessage = async (input: string) => {
if (input.trim() === '') return;

const inputMsg = createMessage<InputMessage>({
type: 'input',
text: input,
});
addMessage(inputMsg);

interpreter.send(inputMsg);
};

return {
messages,
sendMessage,
currentDirectory,
};
}
Loading