opencode-miniterm 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -1
- package/bun.lock +2 -2
- package/package.json +2 -2
- package/src/commands/diff.ts +3 -2
- package/src/commands/init.ts +3 -2
- package/src/commands/new.ts +1 -1
- package/src/commands/sessions.ts +4 -4
- package/src/commands/undo.ts +4 -3
- package/src/config.ts +2 -1
- package/src/index.ts +33 -71
- package/src/commands/kill.ts +0 -33
package/README.md
CHANGED
|
@@ -2,4 +2,167 @@
|
|
|
2
2
|
|
|
3
3
|
A small front-end terminal UI for [OpenCode](https://github.com/anomalyco/opencode).
|
|
4
4
|
|
|
5
|
-
This project is not affiliated with OpenCode.
|
|
5
|
+
> **Note:** This project is not affiliated with OpenCode.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Slash Commands** - Quick access to common operations
|
|
10
|
+
- **File Auto-Completion** - Type `@` followed by file path for intelligent completions
|
|
11
|
+
- **Real-Time Streaming** - See AI responses as they're being generated
|
|
12
|
+
- **Logging Support** - Optional conversation logging for debugging
|
|
13
|
+
- **Keyboard Navigation** - Readline support with history and editing
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### Prerequisites
|
|
18
|
+
|
|
19
|
+
- [OpenCode](https://github.com/anomalyco/opencode) - OpenCode server
|
|
20
|
+
- [Bun](https://bun.sh/) - Required runtime
|
|
21
|
+
|
|
22
|
+
### Install from npm
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g opencode-miniterm
|
|
26
|
+
# or
|
|
27
|
+
pnpm add -g opencode-miniterm
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Install from Source
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone https://github.com/your-repo/opencode-miniterm.git
|
|
34
|
+
cd opencode-miniterm
|
|
35
|
+
bun install
|
|
36
|
+
bun link
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
ocmt
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This will:
|
|
46
|
+
|
|
47
|
+
1. Start the OpenCode server (if not already running)
|
|
48
|
+
2. Create or resume a session for the current directory
|
|
49
|
+
3. Present the interactive prompt
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
Configuration is stored in `~/.config/opencode-miniterm/opencode-miniterm.json`:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"providerID": "opencode",
|
|
58
|
+
"modelID": "big-pickle",
|
|
59
|
+
"agentID": "build",
|
|
60
|
+
"sessionIDs": {
|
|
61
|
+
"/path/to/project1": "session-id-1",
|
|
62
|
+
"/path/to/project2": "session-id-2"
|
|
63
|
+
},
|
|
64
|
+
"loggingEnabled": false
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Environment Variables
|
|
69
|
+
|
|
70
|
+
- `OPENCODE_SERVER_USERNAME` - Server username (default: "opencode")
|
|
71
|
+
- `OPENCODE_SERVER_PASSWORD` - Server password (required if server has auth)
|
|
72
|
+
- `OPENCODE_MT_CONFIG_CONTENT` - Override config as JSON string
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### Basic Interaction
|
|
77
|
+
|
|
78
|
+
Simply type your question or request at the prompt and press Enter:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
> Help me fix the bug in auth.ts
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Slash Commands
|
|
85
|
+
|
|
86
|
+
| Command | Description |
|
|
87
|
+
| ------------------ | ------------------------------------------- |
|
|
88
|
+
| `/help` | Show available commands |
|
|
89
|
+
| `/init` | Analyze project and create/update AGENTS.md |
|
|
90
|
+
| `/new` | Create a new session |
|
|
91
|
+
| `/sessions` | List and switch sessions |
|
|
92
|
+
| `/diff` | Show file additions and deletions |
|
|
93
|
+
| `/undo` | Undo last assistant request |
|
|
94
|
+
| `/details` | Show detailed info for the previous request |
|
|
95
|
+
| `/page` | Page through the detailed info |
|
|
96
|
+
| `/agents` | Show available agents |
|
|
97
|
+
| `/models` | Show available models |
|
|
98
|
+
| `/log` | Enable/disable logging |
|
|
99
|
+
| `/run <cmd>` | Run a shell command from within miniterm |
|
|
100
|
+
| `/exit` or `/quit` | Exit the application |
|
|
101
|
+
|
|
102
|
+
### File References
|
|
103
|
+
|
|
104
|
+
Reference files in your conversation using `@` followed by the path:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
> Review @src/index.ts and suggest improvements
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Tab completion is supported for file paths:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
> @sr<tab> → @src/
|
|
114
|
+
> @src/in<tab> → @src/index.ts
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Keyboard Shortcuts
|
|
118
|
+
|
|
119
|
+
| Key | Action |
|
|
120
|
+
| ---------------------- | ---------------------------- |
|
|
121
|
+
| `↑` / `↓` | Navigate command history |
|
|
122
|
+
| `←` / `→` | Move cursor |
|
|
123
|
+
| `Opt+←` / `Opt+→` | Move by word boundaries |
|
|
124
|
+
| `Tab` | Auto-complete commands/files |
|
|
125
|
+
| `Backspace` / `Delete` | Delete characters |
|
|
126
|
+
| `Esc` | Cancel current request |
|
|
127
|
+
| `Ctrl+C` | Force quit application |
|
|
128
|
+
|
|
129
|
+
## Session Management
|
|
130
|
+
|
|
131
|
+
OpenCode Miniterm automatically manages sessions per directory:
|
|
132
|
+
|
|
133
|
+
- **First Launch**: Creates a new session for the current directory
|
|
134
|
+
- **Subsequent Launches**: Resumes the last session for that directory
|
|
135
|
+
- **New Session**: Use `/new` to create a fresh session
|
|
136
|
+
- **Switch Sessions**: Use `/sessions` to browse and switch between all your sessions
|
|
137
|
+
|
|
138
|
+
## Development
|
|
139
|
+
|
|
140
|
+
### Running Locally
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bun run dev
|
|
144
|
+
# or
|
|
145
|
+
bun src/index.ts
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Build
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bun build src/index.ts --outdir dist
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Type Check
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bun run check
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Formatting
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
bunx prettier --write "**/*.{ts,json,md}"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
ISC
|
package/bun.lock
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "opencode-miniterm",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@opencode-ai/sdk": "^1.2.
|
|
8
|
+
"@opencode-ai/sdk": "^1.2.14",
|
|
9
9
|
"allmark": "^1.0.0",
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
|
49
49
|
|
|
50
|
-
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.
|
|
50
|
+
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.14", "", {}, "sha512-nPkWAmzgPJYyfCJAV4NG7HTfN/iuO3B6fv8sT26NhPiR+EqD9i8sh4X1LwI7wEbbMOwWOX1PhrssW6gXQOOQZQ=="],
|
|
51
51
|
|
|
52
52
|
"@trivago/prettier-plugin-sort-imports": ["@trivago/prettier-plugin-sort-imports@6.0.2", "", { "dependencies": { "@babel/generator": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "javascript-natural-sort": "^0.7.1", "lodash-es": "^4.17.21", "minimatch": "^9.0.0", "parse-imports-exports": "^0.2.4" }, "peerDependencies": { "@vue/compiler-sfc": "3.x", "prettier": "2.x - 3.x", "prettier-plugin-ember-template-tag": ">= 2.0.0", "prettier-plugin-svelte": "3.x", "svelte": "4.x || 5.x" }, "optionalPeers": ["@vue/compiler-sfc", "prettier-plugin-ember-template-tag", "prettier-plugin-svelte", "svelte"] }, "sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA=="],
|
|
53
53
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-miniterm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A small front-end terminal UI for OpenCode",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"typescript": "^5"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@opencode-ai/sdk": "^1.2.
|
|
31
|
+
"@opencode-ai/sdk": "^1.2.14",
|
|
32
32
|
"allmark": "^1.0.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/commands/diff.ts
CHANGED
|
@@ -20,7 +20,8 @@ interface DiffLine {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async function run(client: OpencodeClient, state: State): Promise<void> {
|
|
23
|
-
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
if (!config.sessionIDs[cwd]) {
|
|
24
25
|
console.log("No active session.\n");
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
@@ -28,7 +29,7 @@ async function run(client: OpencodeClient, state: State): Promise<void> {
|
|
|
28
29
|
console.log("Fetching file changes...");
|
|
29
30
|
|
|
30
31
|
const result = await client.session.diff({
|
|
31
|
-
path: { id: config.
|
|
32
|
+
path: { id: config.sessionIDs[cwd] },
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
if (result.error) {
|
package/src/commands/init.ts
CHANGED
|
@@ -14,11 +14,12 @@ let command: Command = {
|
|
|
14
14
|
export default command;
|
|
15
15
|
|
|
16
16
|
async function run(_client: OpencodeClient, _state: State): Promise<void> {
|
|
17
|
-
|
|
17
|
+
const cwd = process.cwd();
|
|
18
|
+
if (!config.sessionIDs[cwd]) return;
|
|
18
19
|
|
|
19
20
|
console.log("Running /init command (analyzing project and creating AGENTS.md)...");
|
|
20
21
|
const result = await _client.session.init({
|
|
21
|
-
path: { id: config.
|
|
22
|
+
path: { id: config.sessionIDs[cwd] },
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
if (result.error) {
|
package/src/commands/new.ts
CHANGED
|
@@ -16,7 +16,7 @@ export default command;
|
|
|
16
16
|
|
|
17
17
|
async function run(client: OpencodeClient, state: State): Promise<void> {
|
|
18
18
|
state.sessionID = await createSession(client);
|
|
19
|
-
config.
|
|
19
|
+
config.sessionIDs[process.cwd()] = state.sessionID;
|
|
20
20
|
saveConfig();
|
|
21
21
|
|
|
22
22
|
await updateSessionTitle();
|
package/src/commands/sessions.ts
CHANGED
|
@@ -44,7 +44,7 @@ async function run(client: OpencodeClient, state: State): Promise<void> {
|
|
|
44
44
|
if (sessions.length === 0) {
|
|
45
45
|
console.log("No sessions found. Creating a new session...");
|
|
46
46
|
state.sessionID = await createSession(client);
|
|
47
|
-
config.
|
|
47
|
+
config.sessionIDs[process.cwd()] = state.sessionID;
|
|
48
48
|
saveConfig();
|
|
49
49
|
console.log(`Created new session: ${state.sessionID}...\n`);
|
|
50
50
|
await updateSessionTitle();
|
|
@@ -135,7 +135,7 @@ async function handleKey(_client: OpencodeClient, key: Key, str?: string) {
|
|
|
135
135
|
readline.cursorTo(process.stdout, 0);
|
|
136
136
|
readline.clearScreenDown(process.stdout);
|
|
137
137
|
if (selected) {
|
|
138
|
-
config.
|
|
138
|
+
config.sessionIDs[process.cwd()] = selected.id;
|
|
139
139
|
saveConfig();
|
|
140
140
|
console.log(`Switched to session: ${selected.id.substring(0, 8)}...`);
|
|
141
141
|
if (selected.title) {
|
|
@@ -222,7 +222,7 @@ function renderSessionList(): void {
|
|
|
222
222
|
const globalIndex = sessionList.indexOf(session);
|
|
223
223
|
const filteredIndex = sessionFilteredIndices.indexOf(globalIndex);
|
|
224
224
|
const isSelected = filteredIndex === selectedSessionIndex;
|
|
225
|
-
const isActive = session.id === config.
|
|
225
|
+
const isActive = session.id === config.sessionIDs[process.cwd()];
|
|
226
226
|
const prefix = isSelected ? " >" : " -";
|
|
227
227
|
const title = session.title || "(no title)";
|
|
228
228
|
const name = isSelected ? `\x1b[33;1m${title}\x1b[0m` : title;
|
|
@@ -250,7 +250,7 @@ function updateSessionFilter(): void {
|
|
|
250
250
|
}
|
|
251
251
|
if (sessionFilteredIndices.length > 0) {
|
|
252
252
|
selectedSessionIndex = sessionFilteredIndices.indexOf(
|
|
253
|
-
sessionList.findIndex((s) => s.id === config.
|
|
253
|
+
sessionList.findIndex((s) => s.id === config.sessionIDs[process.cwd()]),
|
|
254
254
|
);
|
|
255
255
|
if (selectedSessionIndex === -1) selectedSessionIndex = 0;
|
|
256
256
|
}
|
package/src/commands/undo.ts
CHANGED
|
@@ -13,12 +13,13 @@ let command: Command = {
|
|
|
13
13
|
export default command;
|
|
14
14
|
|
|
15
15
|
async function run(client: OpencodeClient, _state: State): Promise<void> {
|
|
16
|
-
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
if (!config.sessionIDs[cwd]) return;
|
|
17
18
|
|
|
18
19
|
console.log("Fetching session messages...");
|
|
19
20
|
|
|
20
21
|
const messagesRes = await client.session.messages({
|
|
21
|
-
path: { id: config.
|
|
22
|
+
path: { id: config.sessionIDs[cwd] },
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
if (messagesRes.error) {
|
|
@@ -49,7 +50,7 @@ async function run(client: OpencodeClient, _state: State): Promise<void> {
|
|
|
49
50
|
console.log(`Reverting last assistant message (${lastMessage.info.id})...`);
|
|
50
51
|
|
|
51
52
|
const revertRes = await client.session.revert({
|
|
52
|
-
path: { id: config.
|
|
53
|
+
path: { id: config.sessionIDs[process.cwd()] },
|
|
53
54
|
body: {
|
|
54
55
|
messageID: lastMessage.info.id,
|
|
55
56
|
},
|
package/src/config.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface Config {
|
|
|
5
5
|
providerID: string;
|
|
6
6
|
modelID: string;
|
|
7
7
|
agentID: string;
|
|
8
|
-
|
|
8
|
+
sessionIDs: Record<string, string>;
|
|
9
9
|
loggingEnabled: boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -15,6 +15,7 @@ export const config: Config = {
|
|
|
15
15
|
providerID: "opencode",
|
|
16
16
|
modelID: "big-pickle",
|
|
17
17
|
agentID: "build",
|
|
18
|
+
sessionIDs: {},
|
|
18
19
|
loggingEnabled: false,
|
|
19
20
|
};
|
|
20
21
|
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
3
|
+
import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk";
|
|
4
4
|
import type { Event, FileDiff, Part, Todo, ToolPart } from "@opencode-ai/sdk";
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
6
5
|
import { mkdir } from "node:fs/promises";
|
|
7
6
|
import { glob } from "node:fs/promises";
|
|
8
7
|
import { stat } from "node:fs/promises";
|
|
@@ -15,7 +14,6 @@ import detailsCommand from "./commands/details";
|
|
|
15
14
|
import diffCommand from "./commands/diff";
|
|
16
15
|
import exitCommand from "./commands/exit";
|
|
17
16
|
import initCommand from "./commands/init";
|
|
18
|
-
import killCommand from "./commands/kill";
|
|
19
17
|
import logCommand, { isLoggingEnabled } from "./commands/log";
|
|
20
18
|
import modelsCommand from "./commands/models";
|
|
21
19
|
import newCommand from "./commands/new";
|
|
@@ -43,7 +41,6 @@ const SLASH_COMMANDS = [
|
|
|
43
41
|
debugCommand,
|
|
44
42
|
logCommand,
|
|
45
43
|
pageCommand,
|
|
46
|
-
killCommand,
|
|
47
44
|
exitCommand,
|
|
48
45
|
quitCommand,
|
|
49
46
|
runCommand,
|
|
@@ -53,7 +50,6 @@ let client: ReturnType<typeof createOpencodeClient>;
|
|
|
53
50
|
|
|
54
51
|
let processing = true;
|
|
55
52
|
let retryInterval: ReturnType<typeof setInterval> | null = null;
|
|
56
|
-
let messageAbortController: AbortController | null = null;
|
|
57
53
|
|
|
58
54
|
interface AccumulatedPart {
|
|
59
55
|
key: string;
|
|
@@ -93,8 +89,17 @@ let logFilePath: string | null = null;
|
|
|
93
89
|
async function main() {
|
|
94
90
|
loadConfig();
|
|
95
91
|
|
|
96
|
-
|
|
92
|
+
console.log(`\n${ansi.BRIGHT_BLACK}Connecting to OpenCode server...${ansi.RESET}\n`);
|
|
97
93
|
|
|
94
|
+
let server: Awaited<ReturnType<typeof createOpencodeServer>> | undefined;
|
|
95
|
+
try {
|
|
96
|
+
server = await createOpencodeServer();
|
|
97
|
+
} catch {
|
|
98
|
+
// Probably the server already exists?
|
|
99
|
+
// Should figure out a better way to check this
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const cwd = process.cwd();
|
|
98
103
|
client = createOpencodeClient({
|
|
99
104
|
baseUrl: SERVER_URL,
|
|
100
105
|
headers: AUTH_PASSWORD
|
|
@@ -102,16 +107,24 @@ async function main() {
|
|
|
102
107
|
Authorization: `Basic ${Buffer.from(`${AUTH_USERNAME}:${AUTH_PASSWORD}`).toString("base64")}`,
|
|
103
108
|
}
|
|
104
109
|
: undefined,
|
|
110
|
+
directory: cwd,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
process.on("SIGINT", () => {
|
|
114
|
+
console.log("\nShutting down...");
|
|
115
|
+
saveConfig();
|
|
116
|
+
server?.close();
|
|
117
|
+
process.exit(0);
|
|
105
118
|
});
|
|
106
119
|
|
|
107
120
|
try {
|
|
108
121
|
let isNewSession = false;
|
|
109
122
|
|
|
110
|
-
const initialSessionID = config.
|
|
123
|
+
const initialSessionID = config.sessionIDs[cwd];
|
|
111
124
|
if (!initialSessionID || !(await validateSession(initialSessionID))) {
|
|
112
125
|
state.sessionID = await createSession();
|
|
113
126
|
isNewSession = true;
|
|
114
|
-
config.
|
|
127
|
+
config.sessionIDs[cwd] = state.sessionID;
|
|
115
128
|
saveConfig();
|
|
116
129
|
} else {
|
|
117
130
|
state.sessionID = initialSessionID;
|
|
@@ -330,21 +343,13 @@ async function main() {
|
|
|
330
343
|
return;
|
|
331
344
|
}
|
|
332
345
|
case "escape": {
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
stopAnimation();
|
|
336
|
-
process.stdout.write(ansi.CURSOR_SHOW);
|
|
337
|
-
process.stdout.write(`\r${ansi.BRIGHT_BLACK}Cancelled request${ansi.RESET}\n`);
|
|
338
|
-
} else {
|
|
339
|
-
inputBuffer = "";
|
|
340
|
-
cursorPosition = 0;
|
|
341
|
-
showCompletions = false;
|
|
342
|
-
completionCycling = false;
|
|
343
|
-
completions = [];
|
|
344
|
-
readline.cursorTo(process.stdout, 0);
|
|
345
|
-
readline.clearScreenDown(process.stdout);
|
|
346
|
-
writePrompt();
|
|
346
|
+
if (state.sessionID) {
|
|
347
|
+
client.session.abort({ path: { id: state.sessionID } }).catch(() => {});
|
|
347
348
|
}
|
|
349
|
+
stopAnimation();
|
|
350
|
+
process.stdout.write(ansi.CURSOR_SHOW);
|
|
351
|
+
process.stdout.write(`\r${ansi.BRIGHT_BLACK}Cancelled request${ansi.RESET}\n`);
|
|
352
|
+
writePrompt();
|
|
348
353
|
return;
|
|
349
354
|
}
|
|
350
355
|
case "return": {
|
|
@@ -400,7 +405,7 @@ async function main() {
|
|
|
400
405
|
writePrompt();
|
|
401
406
|
} catch (error: any) {
|
|
402
407
|
console.error("Error:", error.message);
|
|
403
|
-
|
|
408
|
+
server?.close();
|
|
404
409
|
process.exit(1);
|
|
405
410
|
}
|
|
406
411
|
}
|
|
@@ -409,46 +414,6 @@ async function main() {
|
|
|
409
414
|
// SERVER COMMUNICATION
|
|
410
415
|
// ====================
|
|
411
416
|
|
|
412
|
-
async function startOpenCodeServer() {
|
|
413
|
-
const serverProcess = spawn("opencode", ["serve"], {
|
|
414
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
415
|
-
shell: true,
|
|
416
|
-
cwd: process.cwd(),
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
let started = false;
|
|
420
|
-
|
|
421
|
-
console.log(`\n${ansi.BRIGHT_BLACK}Starting OpenCode server...${ansi.RESET}\n`);
|
|
422
|
-
|
|
423
|
-
serverProcess.stdout.on("data", (data) => {
|
|
424
|
-
if (!started) {
|
|
425
|
-
process.stdout.write(`${ansi.CLEAR_SCREEN_UP}${ansi.CLEAR_FROM_CURSOR}`);
|
|
426
|
-
process.stdout.write(ansi.CURSOR_HOME);
|
|
427
|
-
started = true;
|
|
428
|
-
console.log(`${ansi.BRIGHT_BLACK}Server started, connecting...${ansi.RESET}\n`);
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
serverProcess.on("error", (error) => {
|
|
433
|
-
console.error("Failed to start OpenCode server:", error.message);
|
|
434
|
-
process.exit(1);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
serverProcess.on("exit", (code) => {
|
|
438
|
-
console.log(`OpenCode server exited with code ${code}`);
|
|
439
|
-
process.exit(0);
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
process.on("SIGINT", () => {
|
|
443
|
-
console.log("\nShutting down...");
|
|
444
|
-
saveConfig();
|
|
445
|
-
serverProcess.kill("SIGINT");
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
449
|
-
return serverProcess;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
417
|
async function createSession(): Promise<string> {
|
|
453
418
|
const result = await client.session.create({
|
|
454
419
|
body: {},
|
|
@@ -533,8 +498,6 @@ async function sendMessage(sessionID: string, message: string) {
|
|
|
533
498
|
|
|
534
499
|
await writeToLog(`User: ${message}\n\n`);
|
|
535
500
|
|
|
536
|
-
messageAbortController = new AbortController();
|
|
537
|
-
|
|
538
501
|
const requestStartTime = Date.now();
|
|
539
502
|
|
|
540
503
|
try {
|
|
@@ -547,7 +510,6 @@ async function sendMessage(sessionID: string, message: string) {
|
|
|
547
510
|
},
|
|
548
511
|
parts: [{ type: "text", text: message }],
|
|
549
512
|
},
|
|
550
|
-
signal: messageAbortController.signal,
|
|
551
513
|
});
|
|
552
514
|
|
|
553
515
|
if (result.error) {
|
|
@@ -566,14 +528,14 @@ async function sendMessage(sessionID: string, message: string) {
|
|
|
566
528
|
console.log(`${ansi.BRIGHT_BLACK}Completed in ${durationText}${ansi.RESET}\n`);
|
|
567
529
|
|
|
568
530
|
writePrompt();
|
|
569
|
-
|
|
531
|
+
|
|
532
|
+
// HACK:
|
|
533
|
+
setTimeout(() => {
|
|
534
|
+
readline.cursorTo(process.stdout, 2);
|
|
535
|
+
}, 200);
|
|
570
536
|
} catch (error: any) {
|
|
571
|
-
if (error.name === "AbortError" || messageAbortController?.signal.aborted) {
|
|
572
|
-
throw new Error("Request cancelled");
|
|
573
|
-
}
|
|
574
537
|
throw error;
|
|
575
538
|
} finally {
|
|
576
|
-
messageAbortController = null;
|
|
577
539
|
await closeLogFile();
|
|
578
540
|
}
|
|
579
541
|
}
|
package/src/commands/kill.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { OpencodeClient } from "@opencode-ai/sdk";
|
|
2
|
-
import type { State } from "../index";
|
|
3
|
-
import type { Command } from "../types";
|
|
4
|
-
|
|
5
|
-
let command: Command = {
|
|
6
|
-
name: "/kill",
|
|
7
|
-
description: "Abort a session (e.g. `/kill ses_123`)",
|
|
8
|
-
run,
|
|
9
|
-
running: false,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default command;
|
|
13
|
-
|
|
14
|
-
async function run(client: OpencodeClient, _state: State, input?: string): Promise<void> {
|
|
15
|
-
if (!input) {
|
|
16
|
-
console.log("Usage: /kill <session_id>");
|
|
17
|
-
console.log();
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const result = await client.session.abort({
|
|
22
|
-
path: { id: input },
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
if (result.error) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
`Failed to abort session (${result.response.status}): ${JSON.stringify(result.error)}`,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
console.log(`Session aborted successfully.`);
|
|
32
|
-
console.log();
|
|
33
|
-
}
|