@zhijiewang/openharness 0.1.0 → 0.1.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/.github/workflows/ci.yml +31 -0
- package/README.md +5 -3
- package/dist/components/Markdown.d.ts +4 -0
- package/dist/components/Markdown.d.ts.map +1 -0
- package/dist/components/Markdown.js +47 -0
- package/dist/components/Markdown.js.map +1 -0
- package/dist/components/Messages.d.ts.map +1 -1
- package/dist/components/Messages.js +10 -9
- package/dist/components/Messages.js.map +1 -1
- package/dist/components/PermissionPrompt.d.ts +2 -2
- package/dist/components/PermissionPrompt.d.ts.map +1 -1
- package/dist/components/PermissionPrompt.js +7 -6
- package/dist/components/PermissionPrompt.js.map +1 -1
- package/dist/components/REPL.d.ts +2 -1
- package/dist/components/REPL.d.ts.map +1 -1
- package/dist/components/REPL.js +128 -82
- package/dist/components/REPL.js.map +1 -1
- package/dist/components/Spinner.d.ts +1 -2
- package/dist/components/Spinner.d.ts.map +1 -1
- package/dist/components/Spinner.js +11 -2
- package/dist/components/Spinner.js.map +1 -1
- package/dist/components/StatusBar.d.ts +7 -0
- package/dist/components/StatusBar.d.ts.map +1 -0
- package/dist/components/StatusBar.js +6 -0
- package/dist/components/StatusBar.js.map +1 -0
- package/dist/components/ToolCallDisplay.d.ts +5 -4
- package/dist/components/ToolCallDisplay.d.ts.map +1 -1
- package/dist/components/ToolCallDisplay.js +3 -5
- package/dist/components/ToolCallDisplay.js.map +1 -1
- package/dist/harness/cost.test.d.ts +2 -0
- package/dist/harness/cost.test.d.ts.map +1 -0
- package/dist/harness/cost.test.js +43 -0
- package/dist/harness/cost.test.js.map +1 -0
- package/dist/harness/rules.test.d.ts +2 -0
- package/dist/harness/rules.test.d.ts.map +1 -0
- package/dist/harness/rules.test.js +25 -0
- package/dist/harness/rules.test.js.map +1 -0
- package/dist/harness/session.test.d.ts +2 -0
- package/dist/harness/session.test.d.ts.map +1 -0
- package/dist/harness/session.test.js +37 -0
- package/dist/harness/session.test.js.map +1 -0
- package/dist/tools/file-edit.test.d.ts +2 -0
- package/dist/tools/file-edit.test.d.ts.map +1 -0
- package/dist/tools/file-edit.test.js +35 -0
- package/dist/tools/file-edit.test.js.map +1 -0
- package/dist/tools/web-fetch.test.d.ts +2 -0
- package/dist/tools/web-fetch.test.d.ts.map +1 -0
- package/dist/tools/web-fetch.test.js +27 -0
- package/dist/tools/web-fetch.test.js.map +1 -0
- package/dist/types/permissions.test.d.ts +2 -0
- package/dist/types/permissions.test.d.ts.map +1 -0
- package/dist/types/permissions.test.js +34 -0
- package/dist/types/permissions.test.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
node-version: [18, 20, 22]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: ${{ matrix.node-version }}
|
|
23
|
+
cache: npm
|
|
24
|
+
|
|
25
|
+
- run: npm ci
|
|
26
|
+
|
|
27
|
+
- name: Type check
|
|
28
|
+
run: npx tsc --noEmit
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: npm test
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Open-source terminal coding agent. Build your own Claude Code with any LLM.
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npm install -g openharness
|
|
26
|
+
npm install -g @zhijiewang/openharness
|
|
27
27
|
oh
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -36,13 +36,15 @@ oh --model gpt-4o # use OpenAI (needs OPENAI_API_KEY)
|
|
|
36
36
|
oh --trust # auto-approve all tool calls
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
<!--  -->
|
|
40
|
+
|
|
39
41
|
## Install
|
|
40
42
|
|
|
41
43
|
Requires **Node.js 18+**.
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
# From npm
|
|
45
|
-
npm install -g openharness
|
|
46
|
+
# From npm
|
|
47
|
+
npm install -g @zhijiewang/openharness
|
|
46
48
|
|
|
47
49
|
# From source
|
|
48
50
|
git clone https://github.com/zhijiewong/openharness.git
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../src/components/Markdown.tsx"],"names":[],"mappings":"AAMA,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,kDAoBlE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { marked } from "marked";
|
|
4
|
+
const MD_RE = /[#*`|[\]>\-_~]|\n\n/;
|
|
5
|
+
export default function Markdown({ children }) {
|
|
6
|
+
if (!children.trim())
|
|
7
|
+
return null;
|
|
8
|
+
if (!MD_RE.test(children)) {
|
|
9
|
+
return _jsx(Text, { children: children });
|
|
10
|
+
}
|
|
11
|
+
let tokens;
|
|
12
|
+
try {
|
|
13
|
+
tokens = marked.lexer(children);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return _jsx(Text, { children: children });
|
|
17
|
+
}
|
|
18
|
+
return (_jsx(Box, { flexDirection: "column", children: tokens.map((t, i) => (_jsx(TokenView, { token: t }, i))) }));
|
|
19
|
+
}
|
|
20
|
+
function TokenView({ token }) {
|
|
21
|
+
switch (token.type) {
|
|
22
|
+
case "heading":
|
|
23
|
+
return (_jsxs(Text, { bold: true, color: "cyan", children: ["#".repeat(token.depth ?? 1), " ", cleanInline(token.text ?? "")] }));
|
|
24
|
+
case "paragraph":
|
|
25
|
+
return _jsx(Text, { children: cleanInline(token.text ?? "") });
|
|
26
|
+
case "code":
|
|
27
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [token.lang ? (_jsxs(Text, { dimColor: true, children: ["```", token.lang] })) : (_jsx(Text, { dimColor: true, children: "```" })), _jsx(Text, { dimColor: true, children: token.text ?? "" }), _jsx(Text, { dimColor: true, children: "```" })] }));
|
|
28
|
+
case "list":
|
|
29
|
+
return (_jsx(Box, { flexDirection: "column", children: (token.items ?? []).map((item, i) => (_jsxs(Text, { children: [" ", token.ordered ? `${i + 1}.` : "•", " ", cleanInline(item.text ?? "")] }, i))) }));
|
|
30
|
+
case "blockquote":
|
|
31
|
+
return _jsxs(Text, { dimColor: true, children: ["\u2502 ", cleanInline(token.text ?? "")] });
|
|
32
|
+
case "hr":
|
|
33
|
+
return _jsx(Text, { dimColor: true, children: "─".repeat(40) });
|
|
34
|
+
case "space":
|
|
35
|
+
return null;
|
|
36
|
+
default:
|
|
37
|
+
return token.text ? _jsx(Text, { children: cleanInline(token.text) }) : null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function cleanInline(text) {
|
|
41
|
+
return text
|
|
42
|
+
.replace(/\*\*(.+?)\*\*/g, "$1")
|
|
43
|
+
.replace(/\*(.+?)\*/g, "$1")
|
|
44
|
+
.replace(/`(.+?)`/g, "$1")
|
|
45
|
+
.replace(/\[(.+?)\]\((.+?)\)/g, "$1 ($2)");
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=Markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Markdown.js","sourceRoot":"","sources":["../../src/components/Markdown.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Messages.d.ts","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"Messages.d.ts","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,2CActE"}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
2
3
|
import { Box, Text } from "ink";
|
|
3
4
|
import ToolCallDisplay from "./ToolCallDisplay.js";
|
|
5
|
+
import Markdown from "./Markdown.js";
|
|
4
6
|
export default function Messages({ messages, toolCalls }) {
|
|
5
|
-
return (_jsx(Box, { flexDirection: "column", children: messages.map((msg) =>
|
|
7
|
+
return (_jsx(Box, { flexDirection: "column", children: messages.map((msg, i) => {
|
|
8
|
+
const showDivider = msg.role === "user" && i > 0;
|
|
9
|
+
return (_jsxs(React.Fragment, { children: [showDivider && _jsx(Text, { dimColor: true, children: "─".repeat(50) }), _jsx(MessageRow, { message: msg, toolCalls: toolCalls })] }, msg.uuid));
|
|
10
|
+
}) }));
|
|
6
11
|
}
|
|
7
12
|
function MessageRow({ message, toolCalls, }) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return (_jsxs(Box, { marginY: 0, children: [_jsx(Text, { color: "cyan", bold: true, children: "❯ " }), _jsx(Text, { bold: true, children: content })] }));
|
|
13
|
+
if (message.role === "user") {
|
|
14
|
+
return (_jsxs(Box, { marginY: 0, children: [_jsx(Text, { color: "cyan", bold: true, children: "❯ " }), _jsx(Text, { bold: true, children: message.content })] }));
|
|
11
15
|
}
|
|
12
|
-
if (role === "assistant") {
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [content ? (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(
|
|
16
|
+
if (message.role === "assistant") {
|
|
17
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [message.content ? (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(Markdown, { children: message.content }) })] })) : null, message.toolCalls?.map((tc) => {
|
|
14
18
|
const state = toolCalls.get(tc.id);
|
|
15
19
|
return state ? _jsx(ToolCallDisplay, { toolCall: state }, tc.id) : null;
|
|
16
20
|
})] }));
|
|
17
21
|
}
|
|
18
|
-
if (role === "tool") {
|
|
19
|
-
return null; // Tool results shown inline via ToolCallDisplay
|
|
20
|
-
}
|
|
21
22
|
return null;
|
|
22
23
|
}
|
|
23
24
|
//# sourceMappingURL=Messages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,eAAe,MAAM,sBAAsB,CAAC;AACnD,OAAO,QAAQ,MAAM,eAAe,CAAC;AAOrC,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAiB;IACrE,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YACvB,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;YACjD,OAAO,CACL,MAAC,KAAK,CAAC,QAAQ,eACZ,WAAW,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAQ,EACtD,KAAC,UAAU,IAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAI,KAF/B,GAAG,CAAC,IAAI,CAGZ,CAClB,CAAC;QACJ,CAAC,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,EAClB,OAAO,EACP,SAAS,GAIV;IACC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,CACL,MAAC,GAAG,IAAC,OAAO,EAAE,CAAC,aACb,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,kBAAE,IAAI,GAAQ,EACrC,KAAC,IAAI,IAAC,IAAI,kBAAE,OAAO,CAAC,OAAO,GAAQ,IAC/B,CACP,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACjC,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,aACnC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CACjB,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,EAAC,IAAI,kBAAE,IAAI,GAAQ,EACxC,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,YACrC,KAAC,QAAQ,cAAE,OAAO,CAAC,OAAO,GAAY,GAClC,IACF,CACP,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;oBAC7B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBACnC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAC,eAAe,IAAa,QAAQ,EAAE,KAAK,IAAtB,EAAE,CAAC,EAAE,CAAqB,CAAC,CAAC,CAAC,IAAI,CAAC;gBACzE,CAAC,CAAC,IACE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
type
|
|
1
|
+
type Props = {
|
|
2
2
|
toolName: string;
|
|
3
3
|
description: string;
|
|
4
4
|
riskLevel: string;
|
|
5
5
|
onResolve: (allowed: boolean) => void;
|
|
6
6
|
};
|
|
7
|
-
export default function PermissionPrompt({ toolName, description, riskLevel, onResolve, }:
|
|
7
|
+
export default function PermissionPrompt({ toolName, description, riskLevel, onResolve, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=PermissionPrompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionPrompt.d.ts","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":"AAGA,KAAK,
|
|
1
|
+
{"version":3,"file":"PermissionPrompt.d.ts","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":"AAGA,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAQF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,GACV,EAAE,KAAK,2CAqCP"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
|
-
const
|
|
3
|
+
const RISK_COLORS = {
|
|
4
4
|
low: "green",
|
|
5
5
|
medium: "yellow",
|
|
6
6
|
high: "red",
|
|
7
7
|
};
|
|
8
8
|
export default function PermissionPrompt({ toolName, description, riskLevel, onResolve, }) {
|
|
9
9
|
useInput((input) => {
|
|
10
|
-
|
|
10
|
+
const key = input.toLowerCase();
|
|
11
|
+
if (key === "y")
|
|
11
12
|
onResolve(true);
|
|
12
|
-
if (
|
|
13
|
+
if (key === "n")
|
|
13
14
|
onResolve(false);
|
|
14
15
|
});
|
|
15
|
-
const color =
|
|
16
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: color, paddingX: 1, children: [_jsxs(
|
|
16
|
+
const color = RISK_COLORS[riskLevel] ?? "white";
|
|
17
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: color, paddingX: 2, paddingY: 0, marginY: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: color, bold: true, children: "⚠ " }), _jsx(Text, { bold: true, children: toolName }), _jsxs(Text, { dimColor: true, children: [" ", riskLevel, " risk"] })] }), _jsx(Box, { marginLeft: 2, marginY: 0, children: _jsx(Text, { children: description.slice(0, 300) }) }), _jsx(Box, { marginTop: 0, children: _jsxs(Text, { children: ["Allow? [", _jsx(Text, { color: "green", bold: true, children: "Y" }), "/", _jsx(Text, { color: "red", bold: true, children: "N" }), "]", " "] }) })] }));
|
|
17
18
|
}
|
|
18
19
|
//# sourceMappingURL=PermissionPrompt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionPrompt.js","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAS1C,MAAM,
|
|
1
|
+
{"version":3,"file":"PermissionPrompt.js","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAS1C,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,OAAO;IACZ,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,GACH;IACN,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC;IAEhD,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,OAAO,EACnB,WAAW,EAAE,KAAK,EAClB,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,CAAC,aAEV,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,IAAI,kBACrB,IAAI,GACA,EACP,KAAC,IAAI,IAAC,IAAI,kBAAE,QAAQ,GAAQ,EAC5B,MAAC,IAAI,IAAC,QAAQ,mBAAE,GAAG,EAAE,SAAS,aAAa,IACvC,EAEN,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,YAC5B,KAAC,IAAI,cAAE,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAQ,GACpC,EAEN,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,MAAC,IAAI,2BACK,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,wBAAS,OAAC,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,EAAC,IAAI,wBAAS,OAAE,GAAG,IACzE,GACH,IACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -9,7 +9,8 @@ type REPLProps = {
|
|
|
9
9
|
systemPrompt: string;
|
|
10
10
|
model?: string;
|
|
11
11
|
initialMessages?: Message[];
|
|
12
|
+
resumeSessionId?: string;
|
|
12
13
|
};
|
|
13
|
-
export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, }: REPLProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, resumeSessionId, }: REPLProps): import("react/jsx-runtime").JSX.Element;
|
|
14
15
|
export {};
|
|
15
16
|
//# sourceMappingURL=REPL.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"REPL.d.ts","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"REPL.d.ts","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAW9D,KAAK,SAAS,GAAG;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAkBF,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,cAAc,EACd,YAAY,EACZ,KAAK,EACL,eAAe,EACf,eAAe,GAChB,EAAE,SAAS,2CAoNX"}
|
package/dist/components/REPL.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback } from "react";
|
|
2
|
+
import { useState, useCallback, useRef, useEffect } from "react";
|
|
3
3
|
import { Box, Text, useApp } from "ink";
|
|
4
4
|
import { createAssistantMessage, createUserMessage } from "../types/message.js";
|
|
5
5
|
import { query } from "../query.js";
|
|
6
|
+
import { createSession, saveSession, loadSession } from "../harness/session.js";
|
|
7
|
+
import { CostTracker, estimateCost } from "../harness/cost.js";
|
|
6
8
|
import Messages from "./Messages.js";
|
|
7
9
|
import Spinner from "./Spinner.js";
|
|
8
10
|
import TextInput from "./TextInput.js";
|
|
@@ -15,100 +17,144 @@ const BANNER = ` ___
|
|
|
15
17
|
))(( \\___/|_| |___|_|\\_|_||_/_/ \\_\\_|_\\_|\\_|___|___/___/
|
|
16
18
|
(( ))
|
|
17
19
|
\`--\``;
|
|
18
|
-
export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, }) {
|
|
20
|
+
export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, resumeSessionId, }) {
|
|
19
21
|
const { exit } = useApp();
|
|
20
|
-
|
|
22
|
+
// Session and cost tracking
|
|
23
|
+
const sessionRef = useRef(resumeSessionId
|
|
24
|
+
? (() => { try {
|
|
25
|
+
return loadSession(resumeSessionId);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return createSession("unknown", model ?? "");
|
|
29
|
+
} })()
|
|
30
|
+
: createSession("unknown", model ?? ""));
|
|
31
|
+
const costRef = useRef(new CostTracker());
|
|
32
|
+
const [totalCost, setTotalCost] = useState(0);
|
|
33
|
+
const [sessionId] = useState(sessionRef.current.id);
|
|
34
|
+
const [messages, setMessages] = useState(resumeSessionId ? sessionRef.current.messages : (initialMessages ?? []));
|
|
21
35
|
const [loading, setLoading] = useState(false);
|
|
22
36
|
const [streamingText, setStreamingText] = useState("");
|
|
23
37
|
const [toolCalls, setToolCalls] = useState(new Map());
|
|
24
38
|
const [pendingPermission, setPendingPermission] = useState(null);
|
|
25
39
|
const [error, setError] = useState(null);
|
|
26
40
|
const [currentModel, setCurrentModel] = useState(model ?? "");
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
// Save session on exit
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
return () => {
|
|
44
|
+
sessionRef.current.messages = messages;
|
|
45
|
+
sessionRef.current.totalCost = costRef.current.totalCost;
|
|
46
|
+
try {
|
|
47
|
+
saveSession(sessionRef.current);
|
|
48
|
+
}
|
|
49
|
+
catch { /* ignore */ }
|
|
50
|
+
};
|
|
51
|
+
}, [messages]);
|
|
52
|
+
// Queue prompt submissions — useEffect picks them up for async processing
|
|
53
|
+
const pendingPromptRef = useRef(null);
|
|
54
|
+
const [submitCount, setSubmitCount] = useState(0);
|
|
55
|
+
const messagesRef = useRef(messages);
|
|
56
|
+
messagesRef.current = messages;
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const prompt = pendingPromptRef.current;
|
|
59
|
+
if (!prompt || loading)
|
|
31
60
|
return;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
61
|
+
pendingPromptRef.current = null;
|
|
62
|
+
const run = async () => {
|
|
63
|
+
setLoading(true);
|
|
64
|
+
setStreamingText("");
|
|
65
|
+
setError(null);
|
|
66
|
+
setToolCalls(new Map());
|
|
67
|
+
const askUser = (toolName, description) => {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
setPendingPermission({
|
|
70
|
+
toolName,
|
|
71
|
+
description,
|
|
72
|
+
riskLevel: "medium",
|
|
73
|
+
resolve: (allowed) => {
|
|
74
|
+
setPendingPermission(null);
|
|
75
|
+
resolve(allowed);
|
|
76
|
+
},
|
|
77
|
+
});
|
|
49
78
|
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
};
|
|
80
|
+
const config = {
|
|
81
|
+
provider,
|
|
82
|
+
tools,
|
|
83
|
+
systemPrompt,
|
|
84
|
+
permissionMode,
|
|
85
|
+
askUser,
|
|
86
|
+
};
|
|
87
|
+
let accumulated = "";
|
|
88
|
+
try {
|
|
89
|
+
for await (const event of query(prompt, config, messagesRef.current)) {
|
|
90
|
+
switch (event.type) {
|
|
91
|
+
case "text_delta":
|
|
92
|
+
accumulated += event.content;
|
|
93
|
+
setStreamingText(accumulated);
|
|
94
|
+
break;
|
|
95
|
+
case "tool_call_start":
|
|
96
|
+
setToolCalls((prev) => {
|
|
97
|
+
const next = new Map(prev);
|
|
98
|
+
next.set(event.callId, {
|
|
99
|
+
callId: event.callId,
|
|
100
|
+
toolName: event.toolName,
|
|
101
|
+
status: "running",
|
|
102
|
+
});
|
|
103
|
+
return next;
|
|
74
104
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
break;
|
|
106
|
+
case "tool_call_complete":
|
|
107
|
+
break;
|
|
108
|
+
case "tool_call_end":
|
|
109
|
+
setToolCalls((prev) => {
|
|
110
|
+
const next = new Map(prev);
|
|
111
|
+
next.set(event.callId, {
|
|
112
|
+
callId: event.callId,
|
|
113
|
+
toolName: next.get(event.callId)?.toolName ?? "unknown",
|
|
114
|
+
status: event.isError ? "error" : "done",
|
|
115
|
+
output: event.output,
|
|
116
|
+
});
|
|
117
|
+
return next;
|
|
86
118
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
break;
|
|
120
|
+
case "cost_update":
|
|
121
|
+
setCurrentModel(event.model);
|
|
122
|
+
costRef.current.record("provider", event.model, event.inputTokens, event.outputTokens, event.cost || estimateCost(event.model, event.inputTokens, event.outputTokens));
|
|
123
|
+
setTotalCost(costRef.current.totalCost);
|
|
124
|
+
break;
|
|
125
|
+
case "error":
|
|
126
|
+
setError(event.message);
|
|
127
|
+
break;
|
|
128
|
+
case "turn_complete":
|
|
129
|
+
if (accumulated) {
|
|
130
|
+
setMessages((prev) => [...prev, createAssistantMessage(accumulated)]);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
101
134
|
}
|
|
102
135
|
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
setLoading(false);
|
|
141
|
+
setStreamingText("");
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
run();
|
|
145
|
+
}, [submitCount, loading, provider, tools, systemPrompt, permissionMode]);
|
|
146
|
+
const handleSubmit = useCallback((input) => {
|
|
147
|
+
const trimmed = input.trim();
|
|
148
|
+
if (trimmed === "exit" || trimmed === "quit" || trimmed === "/exit" || trimmed === "/quit") {
|
|
149
|
+
exit();
|
|
150
|
+
return;
|
|
103
151
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}, [provider, tools, systemPrompt, permissionMode, messages, exit]);
|
|
112
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "magenta", children: BANNER }), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "magenta", children: "OpenHarness" }), _jsx(Text, { dimColor: true, children: " v0.1.0" }), _jsx(Text, { color: "cyan", children: currentModel ? ` ${currentModel}` : "" }), _jsx(Text, { dimColor: true, children: ` (${permissionMode})` })] }), _jsx(Text, { dimColor: true, children: "─".repeat(60) })] }), _jsx(Messages, { messages: messages, toolCalls: toolCalls }), loading && streamingText && (_jsxs(Box, { marginY: 0, flexDirection: "column", children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Text, { children: streamingText })] })), loading && !streamingText && _jsx(Spinner, { model: currentModel }), error && (_jsx(Box, { marginY: 1, borderStyle: "round", borderColor: "red", paddingX: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) })), pendingPermission && (_jsx(PermissionPrompt, { toolName: pendingPermission.toolName, description: pendingPermission.description, riskLevel: pendingPermission.riskLevel, onResolve: pendingPermission.resolve })), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { onSubmit: handleSubmit, disabled: loading }) })] }));
|
|
152
|
+
const userMsg = createUserMessage(input);
|
|
153
|
+
setMessages((prev) => [...prev, userMsg]);
|
|
154
|
+
pendingPromptRef.current = input;
|
|
155
|
+
// Increment counter to trigger useEffect (refs don't cause re-renders)
|
|
156
|
+
setSubmitCount((c) => c + 1);
|
|
157
|
+
}, [exit]);
|
|
158
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "magenta", children: BANNER }), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "magenta", children: "OpenHarness" }), _jsx(Text, { dimColor: true, children: " v0.1.0" }), _jsx(Text, { color: "cyan", children: currentModel ? ` ${currentModel}` : "" }), _jsx(Text, { dimColor: true, children: ` (${permissionMode})` })] }), _jsxs(Text, { dimColor: true, children: ["session ", sessionId, totalCost > 0 ? ` | $${totalCost.toFixed(4)}` : ""] }), _jsx(Text, { dimColor: true, children: "─".repeat(60) })] }), _jsx(Messages, { messages: messages, toolCalls: toolCalls }), loading && streamingText && (_jsxs(Box, { marginY: 0, children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Text, { children: streamingText })] })), loading && !streamingText && _jsx(Spinner, { model: currentModel }), error && (_jsx(Box, { marginY: 1, borderStyle: "round", borderColor: "red", paddingX: 1, children: _jsxs(Text, { color: "red", children: ["✗ ", error] }) })), pendingPermission && (_jsx(PermissionPrompt, { toolName: pendingPermission.toolName, description: pendingPermission.description, riskLevel: pendingPermission.riskLevel, onResolve: pendingPermission.resolve })), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { onSubmit: handleSubmit, disabled: loading }) })] }));
|
|
113
159
|
}
|
|
114
160
|
//# sourceMappingURL=REPL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"REPL.js","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"REPL.js","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
type SpinnerProps = {
|
|
2
|
-
label?: string;
|
|
3
2
|
model?: string;
|
|
4
3
|
};
|
|
5
|
-
export default function Spinner({
|
|
4
|
+
export default function Spinner({ model }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
|
|
6
5
|
export {};
|
|
7
6
|
//# sourceMappingURL=Spinner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAIA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAIA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,2CAsBtD"}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
2
3
|
import { Box, Text } from "ink";
|
|
3
4
|
import InkSpinner from "ink-spinner";
|
|
4
|
-
export default function Spinner({
|
|
5
|
-
|
|
5
|
+
export default function Spinner({ model }) {
|
|
6
|
+
const [elapsed, setElapsed] = useState(0);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const start = Date.now();
|
|
9
|
+
const timer = setInterval(() => {
|
|
10
|
+
setElapsed(Math.floor((Date.now() - start) / 1000));
|
|
11
|
+
}, 1000);
|
|
12
|
+
return () => clearInterval(timer);
|
|
13
|
+
}, []);
|
|
14
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: _jsx(InkSpinner, { type: "dots" }) }), _jsxs(Text, { dimColor: true, children: [" ", "Thinking", model ? ` (${model})` : "", elapsed > 0 ? ` ${elapsed}s` : "", "..."] })] }));
|
|
6
15
|
}
|
|
7
16
|
//# sourceMappingURL=Spinner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;AAMrC,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAAE,KAAK,EAAgB;IACrD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE1C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,YACnB,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,GACrB,EACP,MAAC,IAAI,IAAC,QAAQ,mBACX,GAAG,cAAU,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EACvC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,WAC7B,IACH,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":"AAGA,KAAK,KAAK,GAAG;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,KAAK,2CAQjE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
export default function StatusBar({ model, permissionMode }) {
|
|
4
|
+
return (_jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: "─".repeat(60) }) }));
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=StatusBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAOhC,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAS;IAChE,OAAO,CACL,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,QAAQ,kBACX,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GACV,GACH,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
type ToolCallState = {
|
|
1
|
+
export type ToolCallState = {
|
|
2
2
|
callId: string;
|
|
3
3
|
toolName: string;
|
|
4
4
|
status: "running" | "done" | "error";
|
|
5
5
|
output?: string;
|
|
6
|
+
args?: string;
|
|
6
7
|
};
|
|
7
|
-
type
|
|
8
|
+
type Props = {
|
|
8
9
|
toolCall: ToolCallState;
|
|
9
10
|
};
|
|
10
|
-
export default function ToolCallDisplay({ toolCall }:
|
|
11
|
-
export
|
|
11
|
+
export default function ToolCallDisplay({ toolCall }: Props): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
12
13
|
//# sourceMappingURL=ToolCallDisplay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"ToolCallDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA4B1D"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import InkSpinner from "ink-spinner";
|
|
4
|
-
const MAX_OUTPUT_LINES = 8;
|
|
5
4
|
export default function ToolCallDisplay({ toolCall }) {
|
|
6
|
-
const { toolName, status, output } = toolCall;
|
|
7
|
-
|
|
8
|
-
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginY: 0, children: [_jsxs(Box, { children: [icon, _jsx(Text, { color: "yellow", bold: true, children: toolName }), status === "running" && _jsx(Text, { dimColor: true, children: " ..." })] }), output != null && status !== "running" && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: status === "error" ? "red" : "gray", dimColor: true, children: truncateOutput(output, MAX_OUTPUT_LINES) }) }))] }));
|
|
5
|
+
const { toolName, status, output, args } = toolCall;
|
|
6
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginY: 0, children: [_jsxs(Box, { children: [status === "running" ? (_jsxs(Text, { color: "yellow", children: [_jsx(InkSpinner, { type: "dots" }), " "] })) : status === "error" ? (_jsx(Text, { color: "red", children: "✗ " })) : (_jsx(Text, { color: "green", children: "✓ " })), _jsx(Text, { color: "yellow", bold: true, children: toolName }), status === "running" && args && (_jsxs(Text, { dimColor: true, children: [" ", args.slice(0, 60), args.length > 60 ? "..." : ""] }))] }), output != null && status !== "running" && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: status === "error" ? "red" : "gray", dimColor: true, children: truncate(output, 3) }) }))] }));
|
|
9
7
|
}
|
|
10
|
-
function
|
|
8
|
+
function truncate(text, maxLines) {
|
|
11
9
|
const lines = text.split("\n");
|
|
12
10
|
if (lines.length <= maxLines)
|
|
13
11
|
return text;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallDisplay.js","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ToolCallDisplay.js","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;AAcrC,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAS;IACzD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAEpD,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,aACnD,MAAC,GAAG,eACD,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CACtB,MAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,aAAC,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,EAAC,GAAG,IAAQ,CAC5D,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CACvB,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,YAAE,IAAI,GAAQ,CAChC,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,IAAI,GAAQ,CAClC,EACD,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,EAAC,IAAI,kBAAE,QAAQ,GAAQ,EAC1C,MAAM,KAAK,SAAS,IAAI,IAAI,IAAI,CAC/B,MAAC,IAAI,IAAC,QAAQ,mBAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAQ,CAC9E,IACG,EAEL,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,CACzC,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,kBACvD,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GACf,GACH,CACP,IACG,CACP,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK,CAAC,MAAM,SAAS,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.test.d.ts","sourceRoot":"","sources":["../../src/harness/cost.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { CostTracker, estimateCost } from "./cost.js";
|
|
4
|
+
test("record() updates totals", () => {
|
|
5
|
+
const t = new CostTracker();
|
|
6
|
+
t.record("openai", "gpt-4o", 100, 50, 0.01);
|
|
7
|
+
assert.equal(t.events.length, 1);
|
|
8
|
+
assert.equal(t.modelUsage.get("gpt-4o").requests, 1);
|
|
9
|
+
});
|
|
10
|
+
test("totalCost, totalInputTokens, totalOutputTokens", () => {
|
|
11
|
+
const t = new CostTracker();
|
|
12
|
+
t.record("openai", "gpt-4o", 100, 50, 0.01);
|
|
13
|
+
t.record("openai", "gpt-4o", 200, 100, 0.02);
|
|
14
|
+
assert.equal(t.totalCost, 0.03);
|
|
15
|
+
assert.equal(t.totalInputTokens, 300);
|
|
16
|
+
assert.equal(t.totalOutputTokens, 150);
|
|
17
|
+
});
|
|
18
|
+
test("isOverBudget() with budget set", () => {
|
|
19
|
+
const t = new CostTracker(0.01);
|
|
20
|
+
t.record("openai", "gpt-4o", 100, 50, 0.02);
|
|
21
|
+
assert.equal(t.isOverBudget(), true);
|
|
22
|
+
});
|
|
23
|
+
test("isOverBudget() returns false with no budget", () => {
|
|
24
|
+
const t = new CostTracker();
|
|
25
|
+
t.record("openai", "gpt-4o", 100, 50, 999);
|
|
26
|
+
assert.equal(t.isOverBudget(), false);
|
|
27
|
+
});
|
|
28
|
+
test("formatSummary() returns a string", () => {
|
|
29
|
+
const t = new CostTracker(1.0);
|
|
30
|
+
t.record("openai", "gpt-4o", 1000, 500, 0.05);
|
|
31
|
+
const s = t.formatSummary();
|
|
32
|
+
assert.equal(typeof s, "string");
|
|
33
|
+
assert.ok(s.includes("Total cost"));
|
|
34
|
+
assert.ok(s.includes("gpt-4o"));
|
|
35
|
+
});
|
|
36
|
+
test("estimateCost() with known model returns > 0", () => {
|
|
37
|
+
const c = estimateCost("gpt-4o", 1_000_000, 1_000_000);
|
|
38
|
+
assert.ok(c > 0);
|
|
39
|
+
});
|
|
40
|
+
test("estimateCost() with unknown model returns 0", () => {
|
|
41
|
+
assert.equal(estimateCost("unknown-model", 1000, 1000), 0);
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=cost.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.test.js","sourceRoot":"","sources":["../../src/harness/cost.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.test.d.ts","sourceRoot":"","sources":["../../src/harness/rules.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, existsSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { loadRules, createRulesFile } from "./rules.js";
|
|
7
|
+
test("loadRules() returns empty array when no .oh dir exists", () => {
|
|
8
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
9
|
+
const rules = loadRules(tmp);
|
|
10
|
+
assert.deepEqual(rules, []);
|
|
11
|
+
});
|
|
12
|
+
test("createRulesFile() creates .oh/RULES.md", () => {
|
|
13
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
14
|
+
const path = createRulesFile(tmp);
|
|
15
|
+
assert.ok(existsSync(path));
|
|
16
|
+
assert.ok(path.endsWith("RULES.md"));
|
|
17
|
+
});
|
|
18
|
+
test("loadRules() finds created file", () => {
|
|
19
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
20
|
+
createRulesFile(tmp);
|
|
21
|
+
const rules = loadRules(tmp);
|
|
22
|
+
assert.equal(rules.length, 1);
|
|
23
|
+
assert.ok(rules[0].includes("Project Rules"));
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=rules.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.test.js","sourceRoot":"","sources":["../../src/harness/rules.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAExD,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;IAClE,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAClD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC1C,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,eAAe,CAAC,GAAG,CAAC,CAAC;IACrB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.test.d.ts","sourceRoot":"","sources":["../../src/harness/session.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { createSession, saveSession, loadSession, listSessions } from "./session.js";
|
|
7
|
+
test("createSession() creates with id and empty messages", () => {
|
|
8
|
+
const s = createSession("openai", "gpt-4o");
|
|
9
|
+
assert.ok(s.id.length > 0);
|
|
10
|
+
assert.deepEqual(s.messages, []);
|
|
11
|
+
assert.equal(s.provider, "openai");
|
|
12
|
+
assert.equal(s.model, "gpt-4o");
|
|
13
|
+
});
|
|
14
|
+
test("saveSession() + loadSession() roundtrip", () => {
|
|
15
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
16
|
+
const s = createSession("anthropic", "claude-sonnet-4-6");
|
|
17
|
+
saveSession(s, tmp);
|
|
18
|
+
const loaded = loadSession(s.id, tmp);
|
|
19
|
+
assert.equal(loaded.id, s.id);
|
|
20
|
+
assert.equal(loaded.provider, "anthropic");
|
|
21
|
+
assert.equal(loaded.model, "claude-sonnet-4-6");
|
|
22
|
+
});
|
|
23
|
+
test("listSessions() returns saved sessions sorted by updatedAt", async () => {
|
|
24
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
25
|
+
const s1 = createSession("openai", "gpt-4o");
|
|
26
|
+
const s2 = createSession("openai", "gpt-4o-mini");
|
|
27
|
+
saveSession(s1, tmp);
|
|
28
|
+
// Small delay so updatedAt differs
|
|
29
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
30
|
+
saveSession(s2, tmp);
|
|
31
|
+
const list = listSessions(tmp);
|
|
32
|
+
assert.equal(list.length, 2);
|
|
33
|
+
// Most recent first
|
|
34
|
+
assert.equal(list[0].id, s2.id);
|
|
35
|
+
assert.equal(list[1].id, s1.id);
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=session.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.test.js","sourceRoot":"","sources":["../../src/harness/session.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAErF,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,CAAC,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACnD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,aAAa,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAC1D,WAAW,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAClD,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACrB,mCAAmC;IACnC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAErB,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7B,oBAAoB;IACpB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-edit.test.d.ts","sourceRoot":"","sources":["../../src/tools/file-edit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, writeFileSync, readFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { FileEditTool } from "./FileEditTool/index.js";
|
|
7
|
+
const ctx = { workingDir: process.cwd() };
|
|
8
|
+
test("replace string in file", async () => {
|
|
9
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
10
|
+
const f = join(tmp, "test.txt");
|
|
11
|
+
writeFileSync(f, "hello world");
|
|
12
|
+
const r = await FileEditTool.call({ file_path: f, old_string: "hello", new_string: "goodbye" }, ctx);
|
|
13
|
+
assert.equal(r.isError, false);
|
|
14
|
+
assert.equal(readFileSync(f, "utf-8"), "goodbye world");
|
|
15
|
+
});
|
|
16
|
+
test("returns error when old_string not found", async () => {
|
|
17
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
18
|
+
const f = join(tmp, "test.txt");
|
|
19
|
+
writeFileSync(f, "hello world");
|
|
20
|
+
const r = await FileEditTool.call({ file_path: f, old_string: "missing", new_string: "x" }, ctx);
|
|
21
|
+
assert.equal(r.isError, true);
|
|
22
|
+
assert.ok(r.output.includes("not found"));
|
|
23
|
+
});
|
|
24
|
+
test("returns error when old_string not unique unless replace_all", async () => {
|
|
25
|
+
const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
|
|
26
|
+
const f = join(tmp, "test.txt");
|
|
27
|
+
writeFileSync(f, "aaa bbb aaa");
|
|
28
|
+
const r1 = await FileEditTool.call({ file_path: f, old_string: "aaa", new_string: "ccc" }, ctx);
|
|
29
|
+
assert.equal(r1.isError, true);
|
|
30
|
+
assert.ok(r1.output.includes("not unique"));
|
|
31
|
+
const r2 = await FileEditTool.call({ file_path: f, old_string: "aaa", new_string: "ccc", replace_all: true }, ctx);
|
|
32
|
+
assert.equal(r2.isError, false);
|
|
33
|
+
assert.equal(readFileSync(f, "utf-8"), "ccc bbb ccc");
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=file-edit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-edit.test.js","sourceRoot":"","sources":["../../src/tools/file-edit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fetch.test.d.ts","sourceRoot":"","sources":["../../src/tools/web-fetch.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { WebFetchTool } from "./WebFetchTool/index.js";
|
|
4
|
+
const ctx = { workingDir: process.cwd() };
|
|
5
|
+
test("blocks localhost", async () => {
|
|
6
|
+
const r = await WebFetchTool.call({ url: "http://localhost:8080/secret" }, ctx);
|
|
7
|
+
assert.equal(r.isError, true);
|
|
8
|
+
assert.ok(r.output.includes("blocked"));
|
|
9
|
+
});
|
|
10
|
+
test("blocks 192.168.x.x", async () => {
|
|
11
|
+
const r = await WebFetchTool.call({ url: "http://192.168.1.1/" }, ctx);
|
|
12
|
+
assert.equal(r.isError, true);
|
|
13
|
+
assert.ok(r.output.includes("blocked"));
|
|
14
|
+
});
|
|
15
|
+
test("blocks .internal hostnames", async () => {
|
|
16
|
+
const r = await WebFetchTool.call({ url: "http://app.internal/api" }, ctx);
|
|
17
|
+
assert.equal(r.isError, true);
|
|
18
|
+
assert.ok(r.output.includes("blocked"));
|
|
19
|
+
});
|
|
20
|
+
test("allows normal https URLs (will fail to connect but not blocked)", async () => {
|
|
21
|
+
// Use a URL that won't actually resolve to avoid network calls,
|
|
22
|
+
// but the SSRF check itself should pass (error will be a fetch error, not "blocked")
|
|
23
|
+
const r = await WebFetchTool.call({ url: "https://example.invalid/page" }, ctx);
|
|
24
|
+
// Should NOT be the SSRF block message
|
|
25
|
+
assert.ok(!r.output.includes("private/internal hosts is blocked"));
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=web-fetch.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fetch.test.js","sourceRoot":"","sources":["../../src/tools/web-fetch.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,GAAG,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;AAE1C,IAAI,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;IAClC,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;IAChF,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;IACpC,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,qBAAqB,EAAE,EAAE,GAAG,CAAC,CAAC;IACvE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,yBAAyB,EAAE,EAAE,GAAG,CAAC,CAAC;IAC3E,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;IACjF,gEAAgE;IAChE,qFAAqF;IACrF,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;IAChF,uCAAuC;IACvC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.test.d.ts","sourceRoot":"","sources":["../../src/types/permissions.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { checkPermission } from "./permissions.js";
|
|
4
|
+
test("trust mode allows everything", () => {
|
|
5
|
+
const r = checkPermission("trust", "high", false);
|
|
6
|
+
assert.equal(r.allowed, true);
|
|
7
|
+
assert.equal(r.reason, "trust-mode");
|
|
8
|
+
});
|
|
9
|
+
test("deny mode blocks non-low", () => {
|
|
10
|
+
const r = checkPermission("deny", "medium", false);
|
|
11
|
+
assert.equal(r.allowed, false);
|
|
12
|
+
assert.equal(r.reason, "deny-mode");
|
|
13
|
+
});
|
|
14
|
+
test("deny mode allows low+readonly", () => {
|
|
15
|
+
const r = checkPermission("deny", "low", true);
|
|
16
|
+
assert.equal(r.allowed, true);
|
|
17
|
+
assert.equal(r.reason, "auto-approved");
|
|
18
|
+
});
|
|
19
|
+
test("ask mode: low+readonly auto-approved", () => {
|
|
20
|
+
const r = checkPermission("ask", "low", true);
|
|
21
|
+
assert.equal(r.allowed, true);
|
|
22
|
+
assert.equal(r.reason, "auto-approved");
|
|
23
|
+
});
|
|
24
|
+
test("ask mode: high risk returns needs-approval", () => {
|
|
25
|
+
const r = checkPermission("ask", "high", false);
|
|
26
|
+
assert.equal(r.allowed, false);
|
|
27
|
+
assert.equal(r.reason, "needs-approval");
|
|
28
|
+
});
|
|
29
|
+
test("ask mode: medium risk returns needs-approval", () => {
|
|
30
|
+
const r = checkPermission("ask", "medium", false);
|
|
31
|
+
assert.equal(r.allowed, false);
|
|
32
|
+
assert.equal(r.reason, "needs-approval");
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=permissions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.test.js","sourceRoot":"","sources":["../../src/types/permissions.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;IACxC,MAAM,CAAC,GAAG,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACpC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE;IACzC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAChD,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhijiewang/openharness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Open-source terminal coding agent. Build your own Claude Code with any LLM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
"start": "node dist/main.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@types/marked": "^5.0.2",
|
|
20
21
|
"chalk": "^5.4.1",
|
|
21
22
|
"commander": "^13.0.0",
|
|
22
23
|
"ink": "^5.2.0",
|
|
23
24
|
"ink-spinner": "^5.0.0",
|
|
24
25
|
"ink-text-input": "^6.0.0",
|
|
26
|
+
"marked": "^17.0.5",
|
|
25
27
|
"react": "^18.3.1",
|
|
26
28
|
"yaml": "^2.7.0",
|
|
27
29
|
"zod": "^3.24.0"
|