@goondan/openharness-cli 0.0.1-alpha4 → 0.1.0

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/dist/bin.js CHANGED
File without changes
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
- export { main } from "./cli.js";
2
- //# sourceMappingURL=index.d.ts.map
1
+ #!/usr/bin/env node
package/dist/index.js CHANGED
@@ -1,2 +1,260 @@
1
- export { main } from "./cli.js";
2
- //# sourceMappingURL=index.js.map
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import * as fs from "fs";
5
+
6
+ // src/commands/run.ts
7
+ import * as path2 from "path";
8
+ import { createHarness } from "@goondan/openharness";
9
+
10
+ // src/env-loader.ts
11
+ import * as path from "path";
12
+ import dotenv from "dotenv";
13
+ function loadEnv(workdir) {
14
+ const envPath = path.join(workdir, ".env");
15
+ dotenv.config({ path: envPath, override: false });
16
+ }
17
+
18
+ // src/config-loader.ts
19
+ import { pathToFileURL } from "url";
20
+ async function loadConfig(configPath) {
21
+ const fileUrl = pathToFileURL(configPath).href;
22
+ const mod = await import(fileUrl);
23
+ const config = mod.default ?? mod;
24
+ if (config === null || typeof config !== "object" || !("agents" in config) || typeof config.agents !== "object") {
25
+ throw new Error(
26
+ `Config file "${configPath}" must export a default HarnessConfig with an "agents" property.`
27
+ );
28
+ }
29
+ return config;
30
+ }
31
+
32
+ // src/commands/run.ts
33
+ async function runCommand(text, options) {
34
+ const workdir = options.workdir ?? process.cwd();
35
+ const configPath = options.config ? path2.resolve(workdir, options.config) : path2.resolve(workdir, "harness.config.ts");
36
+ loadEnv(workdir);
37
+ let config;
38
+ try {
39
+ config = await loadConfig(configPath);
40
+ } catch (err) {
41
+ console.error(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
42
+ process.exit(1);
43
+ }
44
+ const agentNames = Object.keys(config.agents);
45
+ let agentName;
46
+ if (options.agent) {
47
+ if (!config.agents[options.agent]) {
48
+ console.error(`Unknown agent: "${options.agent}". Available agents: ${agentNames.join(", ")}`);
49
+ process.exit(2);
50
+ }
51
+ agentName = options.agent;
52
+ } else if (agentNames.length === 1) {
53
+ agentName = agentNames[0];
54
+ } else {
55
+ console.error(
56
+ `Multiple agents defined. Specify one with --agent. Available agents: ${agentNames.join(", ")}`
57
+ );
58
+ process.exit(2);
59
+ }
60
+ let harness;
61
+ try {
62
+ harness = await createHarness(config);
63
+ } catch (err) {
64
+ console.error(`Error creating harness: ${err instanceof Error ? err.message : String(err)}`);
65
+ process.exit(1);
66
+ }
67
+ try {
68
+ const result = await harness.processTurn(agentName, text, {
69
+ conversationId: options.conversation
70
+ });
71
+ if (result.text) {
72
+ process.stdout.write(result.text);
73
+ process.stdout.write("\n");
74
+ }
75
+ } catch (err) {
76
+ console.error(`Runtime error: ${err instanceof Error ? err.message : String(err)}`);
77
+ process.exit(1);
78
+ } finally {
79
+ await harness.close();
80
+ }
81
+ }
82
+
83
+ // src/commands/repl.ts
84
+ import * as path3 from "path";
85
+ import * as readline from "readline";
86
+ import { randomUUID } from "crypto";
87
+ import { createHarness as createHarness2 } from "@goondan/openharness";
88
+ async function replCommand(options) {
89
+ const workdir = options.workdir ?? process.cwd();
90
+ const configPath = options.config ? path3.resolve(workdir, options.config) : path3.resolve(workdir, "harness.config.ts");
91
+ loadEnv(workdir);
92
+ let config;
93
+ try {
94
+ config = await loadConfig(configPath);
95
+ } catch (err) {
96
+ console.error(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
97
+ process.exit(1);
98
+ }
99
+ const agentNames = Object.keys(config.agents);
100
+ let agentName;
101
+ if (options.agent) {
102
+ if (!config.agents[options.agent]) {
103
+ console.error(`Unknown agent: "${options.agent}". Available agents: ${agentNames.join(", ")}`);
104
+ process.exit(2);
105
+ }
106
+ agentName = options.agent;
107
+ } else if (agentNames.length === 1) {
108
+ agentName = agentNames[0];
109
+ } else {
110
+ console.error(
111
+ `Multiple agents defined. Specify one with --agent. Available agents: ${agentNames.join(", ")}`
112
+ );
113
+ process.exit(2);
114
+ }
115
+ let harness;
116
+ try {
117
+ harness = await createHarness2(config);
118
+ } catch (err) {
119
+ console.error(`Error creating harness: ${err instanceof Error ? err.message : String(err)}`);
120
+ process.exit(1);
121
+ }
122
+ const conversationId = options.conversation ?? randomUUID();
123
+ const rl = readline.createInterface({
124
+ input: process.stdin,
125
+ output: process.stdout
126
+ });
127
+ const prompt = () => {
128
+ rl.question("> ", async (line) => {
129
+ const trimmed = line.trim();
130
+ if (trimmed === "exit" || trimmed === "quit") {
131
+ rl.close();
132
+ await harness.close();
133
+ return;
134
+ }
135
+ if (!trimmed) {
136
+ prompt();
137
+ return;
138
+ }
139
+ try {
140
+ const result = await harness.processTurn(agentName, trimmed, { conversationId });
141
+ if (result.text) {
142
+ console.log(result.text);
143
+ }
144
+ } catch (err) {
145
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
146
+ }
147
+ prompt();
148
+ });
149
+ };
150
+ rl.on("SIGINT", () => {
151
+ console.log("\nExiting...");
152
+ rl.close();
153
+ harness.close().then(() => process.exit(0)).catch(() => process.exit(1));
154
+ });
155
+ rl.on("close", () => {
156
+ harness.close().catch(() => {
157
+ });
158
+ });
159
+ console.log(`OpenHarness REPL - Agent: ${agentName} (conversation: ${conversationId})`);
160
+ console.log('Type "exit" or press Ctrl+C to quit.\n');
161
+ prompt();
162
+ }
163
+
164
+ // src/index.ts
165
+ var CLI_VERSION = JSON.parse(
166
+ fs.readFileSync(new URL("../package.json", import.meta.url), "utf8")
167
+ );
168
+ function parseArgs(argv) {
169
+ const args = argv.slice(2);
170
+ let i = 0;
171
+ const result = { command: "repl" };
172
+ if (args[i] && !args[i].startsWith("-")) {
173
+ const cmd = args[i];
174
+ if (cmd === "run") {
175
+ result.command = "run";
176
+ i++;
177
+ if (args[i] && !args[i].startsWith("-")) {
178
+ result.text = args[i];
179
+ i++;
180
+ }
181
+ } else if (cmd === "repl") {
182
+ result.command = "repl";
183
+ i++;
184
+ }
185
+ }
186
+ while (i < args.length) {
187
+ const arg = args[i];
188
+ if (arg === "--workdir" || arg === "-w") {
189
+ result.workdir = args[++i];
190
+ } else if (arg.startsWith("--workdir=")) {
191
+ result.workdir = arg.slice("--workdir=".length);
192
+ } else if (arg === "--config" || arg === "-c") {
193
+ result.config = args[++i];
194
+ } else if (arg.startsWith("--config=")) {
195
+ result.config = arg.slice("--config=".length);
196
+ } else if (arg === "--agent" || arg === "-a") {
197
+ result.agent = args[++i];
198
+ } else if (arg.startsWith("--agent=")) {
199
+ result.agent = arg.slice("--agent=".length);
200
+ } else if (arg === "--conversation") {
201
+ result.conversation = args[++i];
202
+ } else if (arg.startsWith("--conversation=")) {
203
+ result.conversation = arg.slice("--conversation=".length);
204
+ } else if (arg === "--max-steps") {
205
+ result.maxSteps = parseInt(args[++i] ?? "25", 10);
206
+ } else if (arg.startsWith("--max-steps=")) {
207
+ result.maxSteps = parseInt(arg.slice("--max-steps=".length), 10);
208
+ } else if (arg === "--help" || arg === "-h") {
209
+ printHelp();
210
+ process.exit(0);
211
+ } else if (arg === "--version" || arg === "-v") {
212
+ console.log(CLI_VERSION.version);
213
+ process.exit(0);
214
+ }
215
+ i++;
216
+ }
217
+ return result;
218
+ }
219
+ function printHelp() {
220
+ console.log(`
221
+ OpenHarness CLI (oh)
222
+
223
+ Usage:
224
+ oh Start REPL (default)
225
+ oh repl Start REPL
226
+ oh run "<text>" Run a single turn
227
+
228
+ Options:
229
+ --workdir, -w <dir> Working directory (default: cwd)
230
+ --config, -c <file> Config file (default: harness.config.ts)
231
+ --agent, -a <name> Agent name (required if multiple agents)
232
+ --conversation <id> Conversation ID
233
+ --max-steps <n> Maximum steps per turn
234
+ --help, -h Show this help
235
+ --version, -v Show version
236
+ `);
237
+ }
238
+ async function main() {
239
+ const parsed = parseArgs(process.argv);
240
+ const opts = {
241
+ workdir: parsed.workdir,
242
+ config: parsed.config,
243
+ agent: parsed.agent,
244
+ conversation: parsed.conversation,
245
+ maxSteps: parsed.maxSteps
246
+ };
247
+ if (parsed.command === "run") {
248
+ if (!parsed.text) {
249
+ console.error('Usage: oh run "<text>"');
250
+ process.exit(2);
251
+ }
252
+ await runCommand(parsed.text, opts);
253
+ } else {
254
+ await replCommand(opts);
255
+ }
256
+ }
257
+ main().catch((err) => {
258
+ console.error(err instanceof Error ? err.message : String(err));
259
+ process.exit(1);
260
+ });
package/package.json CHANGED
@@ -1,51 +1,34 @@
1
1
  {
2
2
  "name": "@goondan/openharness-cli",
3
- "version": "0.0.1-alpha4",
4
- "description": "CLI for running and debugging OpenHarness agents locally",
3
+ "version": "0.1.0",
5
4
  "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "files": [
9
- "dist"
10
- ],
11
- "homepage": "https://github.com/goondan/openharness#readme",
12
- "bugs": {
13
- "url": "https://github.com/goondan/openharness/issues"
14
- },
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/goondan/openharness.git",
18
- "directory": "packages/cli"
19
- },
20
- "publishConfig": {
21
- "access": "public"
22
- },
23
- "keywords": [
24
- "agent",
25
- "cli",
26
- "harness",
27
- "llm"
28
- ],
29
5
  "bin": {
30
- "oh": "dist/bin.js"
6
+ "oh": "./dist/index.js"
31
7
  },
8
+ "types": "./dist/index.d.ts",
32
9
  "exports": {
33
10
  ".": {
34
- "types": "./dist/index.d.ts",
35
- "import": "./dist/index.js"
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
36
13
  }
37
14
  },
38
- "scripts": {
39
- "build": "pnpm clean && tsc -p tsconfig.json",
40
- "postbuild": "node ./scripts/link-global-bin.mjs",
41
- "prepack": "pnpm build",
42
- "link:global": "node ./scripts/link-global-bin.mjs",
43
- "typecheck": "tsc -p tsconfig.json --noEmit",
44
- "test": "node -e \"process.exit(0)\"",
45
- "start": "node dist/bin.js",
46
- "clean": "rm -rf dist"
47
- },
15
+ "files": [
16
+ "dist"
17
+ ],
48
18
  "dependencies": {
49
- "@goondan/openharness": "^0.0.1-alpha4"
19
+ "dotenv": "^16.4.0",
20
+ "@goondan/openharness": "0.1.0",
21
+ "@goondan/openharness-types": "0.1.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.7.0",
25
+ "tsup": "^8.4.0",
26
+ "vitest": "^3.0.0",
27
+ "@types/node": "^22.0.0"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup src/index.ts --format esm --dts",
31
+ "typecheck": "tsc --noEmit",
32
+ "test": "vitest run"
50
33
  }
51
- }
34
+ }
package/README.md DELETED
@@ -1,11 +0,0 @@
1
- # @goondan/openharness-cli
2
-
3
- OpenHarness runtime을 로컬에서 실행하고 디버깅할 수 있는 CLI 패키지입니다.
4
-
5
- ## Install
6
-
7
- ```bash
8
- npm install --global @goondan/openharness-cli
9
- ```
10
-
11
- 설치 후 `oh` 명령으로 실행할 수 있습니다.