@goondan/openharness-cli 0.1.8 → 0.1.10
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/index.d.ts +1 -0
- package/dist/index.js +282 -0
- package/package.json +3 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
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 loadWithJiti(configPath) {
|
|
21
|
+
const { createJiti } = await import("jiti");
|
|
22
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
23
|
+
return jiti.import(configPath);
|
|
24
|
+
}
|
|
25
|
+
async function loadWithNativeImport(configPath) {
|
|
26
|
+
const fileUrl = pathToFileURL(configPath).href;
|
|
27
|
+
return import(fileUrl);
|
|
28
|
+
}
|
|
29
|
+
async function loadConfig(configPath) {
|
|
30
|
+
let mod;
|
|
31
|
+
try {
|
|
32
|
+
mod = await loadWithJiti(configPath);
|
|
33
|
+
} catch {
|
|
34
|
+
try {
|
|
35
|
+
mod = await loadWithNativeImport(configPath);
|
|
36
|
+
} catch (nativeError) {
|
|
37
|
+
if (configPath.endsWith(".ts")) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Failed to load TypeScript config "${configPath}". TypeScript config\uB97C \uB85C\uB4DC\uD558\uB824\uBA74 jiti, tsx, \uB610\uB294 ts-node\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. Cause: ${nativeError instanceof Error ? nativeError.message : String(nativeError)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
throw nativeError;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const config = mod.default ?? mod;
|
|
46
|
+
if (config === null || typeof config !== "object" || !("agents" in config) || typeof config.agents !== "object") {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Config file "${configPath}" must export a default HarnessConfig with an "agents" property.`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/commands/run.ts
|
|
55
|
+
async function runCommand(text, options) {
|
|
56
|
+
const workdir = options.workdir ?? process.cwd();
|
|
57
|
+
const configPath = options.config ? path2.resolve(workdir, options.config) : path2.resolve(workdir, "harness.config.ts");
|
|
58
|
+
loadEnv(workdir);
|
|
59
|
+
let config;
|
|
60
|
+
try {
|
|
61
|
+
config = await loadConfig(configPath);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
const agentNames = Object.keys(config.agents);
|
|
67
|
+
let agentName;
|
|
68
|
+
if (options.agent) {
|
|
69
|
+
if (!config.agents[options.agent]) {
|
|
70
|
+
console.error(`Unknown agent: "${options.agent}". Available agents: ${agentNames.join(", ")}`);
|
|
71
|
+
process.exit(2);
|
|
72
|
+
}
|
|
73
|
+
agentName = options.agent;
|
|
74
|
+
} else if (agentNames.length === 1) {
|
|
75
|
+
agentName = agentNames[0];
|
|
76
|
+
} else {
|
|
77
|
+
console.error(
|
|
78
|
+
`Multiple agents defined. Specify one with --agent. Available agents: ${agentNames.join(", ")}`
|
|
79
|
+
);
|
|
80
|
+
process.exit(2);
|
|
81
|
+
}
|
|
82
|
+
let harness;
|
|
83
|
+
try {
|
|
84
|
+
harness = await createHarness(config);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(`Error creating harness: ${err instanceof Error ? err.message : String(err)}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const result = await harness.processTurn(agentName, text, {
|
|
91
|
+
conversationId: options.conversation
|
|
92
|
+
});
|
|
93
|
+
if (result.text) {
|
|
94
|
+
process.stdout.write(result.text);
|
|
95
|
+
process.stdout.write("\n");
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error(`Runtime error: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
} finally {
|
|
101
|
+
await harness.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/commands/repl.ts
|
|
106
|
+
import * as path3 from "path";
|
|
107
|
+
import * as readline from "readline";
|
|
108
|
+
import { randomUUID } from "crypto";
|
|
109
|
+
import { createHarness as createHarness2 } from "@goondan/openharness";
|
|
110
|
+
async function replCommand(options) {
|
|
111
|
+
const workdir = options.workdir ?? process.cwd();
|
|
112
|
+
const configPath = options.config ? path3.resolve(workdir, options.config) : path3.resolve(workdir, "harness.config.ts");
|
|
113
|
+
loadEnv(workdir);
|
|
114
|
+
let config;
|
|
115
|
+
try {
|
|
116
|
+
config = await loadConfig(configPath);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const agentNames = Object.keys(config.agents);
|
|
122
|
+
let agentName;
|
|
123
|
+
if (options.agent) {
|
|
124
|
+
if (!config.agents[options.agent]) {
|
|
125
|
+
console.error(`Unknown agent: "${options.agent}". Available agents: ${agentNames.join(", ")}`);
|
|
126
|
+
process.exit(2);
|
|
127
|
+
}
|
|
128
|
+
agentName = options.agent;
|
|
129
|
+
} else if (agentNames.length === 1) {
|
|
130
|
+
agentName = agentNames[0];
|
|
131
|
+
} else {
|
|
132
|
+
console.error(
|
|
133
|
+
`Multiple agents defined. Specify one with --agent. Available agents: ${agentNames.join(", ")}`
|
|
134
|
+
);
|
|
135
|
+
process.exit(2);
|
|
136
|
+
}
|
|
137
|
+
let harness;
|
|
138
|
+
try {
|
|
139
|
+
harness = await createHarness2(config);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error(`Error creating harness: ${err instanceof Error ? err.message : String(err)}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
const conversationId = options.conversation ?? randomUUID();
|
|
145
|
+
const rl = readline.createInterface({
|
|
146
|
+
input: process.stdin,
|
|
147
|
+
output: process.stdout
|
|
148
|
+
});
|
|
149
|
+
const prompt = () => {
|
|
150
|
+
rl.question("> ", async (line) => {
|
|
151
|
+
const trimmed = line.trim();
|
|
152
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
153
|
+
rl.close();
|
|
154
|
+
await harness.close();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!trimmed) {
|
|
158
|
+
prompt();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const result = await harness.processTurn(agentName, trimmed, { conversationId });
|
|
163
|
+
if (result.text) {
|
|
164
|
+
console.log(result.text);
|
|
165
|
+
}
|
|
166
|
+
} catch (err) {
|
|
167
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
168
|
+
}
|
|
169
|
+
prompt();
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
rl.on("SIGINT", () => {
|
|
173
|
+
console.log("\nExiting...");
|
|
174
|
+
rl.close();
|
|
175
|
+
harness.close().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
176
|
+
});
|
|
177
|
+
rl.on("close", () => {
|
|
178
|
+
harness.close().catch(() => {
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
console.log(`OpenHarness REPL - Agent: ${agentName} (conversation: ${conversationId})`);
|
|
182
|
+
console.log('Type "exit" or press Ctrl+C to quit.\n');
|
|
183
|
+
prompt();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/index.ts
|
|
187
|
+
var CLI_VERSION = JSON.parse(
|
|
188
|
+
fs.readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
189
|
+
);
|
|
190
|
+
function parseArgs(argv) {
|
|
191
|
+
const args = argv.slice(2);
|
|
192
|
+
let i = 0;
|
|
193
|
+
const result = { command: "repl" };
|
|
194
|
+
if (args[i] && !args[i].startsWith("-")) {
|
|
195
|
+
const cmd = args[i];
|
|
196
|
+
if (cmd === "run") {
|
|
197
|
+
result.command = "run";
|
|
198
|
+
i++;
|
|
199
|
+
if (args[i] && !args[i].startsWith("-")) {
|
|
200
|
+
result.text = args[i];
|
|
201
|
+
i++;
|
|
202
|
+
}
|
|
203
|
+
} else if (cmd === "repl") {
|
|
204
|
+
result.command = "repl";
|
|
205
|
+
i++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
while (i < args.length) {
|
|
209
|
+
const arg = args[i];
|
|
210
|
+
if (arg === "--workdir" || arg === "-w") {
|
|
211
|
+
result.workdir = args[++i];
|
|
212
|
+
} else if (arg.startsWith("--workdir=")) {
|
|
213
|
+
result.workdir = arg.slice("--workdir=".length);
|
|
214
|
+
} else if (arg === "--config" || arg === "-c") {
|
|
215
|
+
result.config = args[++i];
|
|
216
|
+
} else if (arg.startsWith("--config=")) {
|
|
217
|
+
result.config = arg.slice("--config=".length);
|
|
218
|
+
} else if (arg === "--agent" || arg === "-a") {
|
|
219
|
+
result.agent = args[++i];
|
|
220
|
+
} else if (arg.startsWith("--agent=")) {
|
|
221
|
+
result.agent = arg.slice("--agent=".length);
|
|
222
|
+
} else if (arg === "--conversation") {
|
|
223
|
+
result.conversation = args[++i];
|
|
224
|
+
} else if (arg.startsWith("--conversation=")) {
|
|
225
|
+
result.conversation = arg.slice("--conversation=".length);
|
|
226
|
+
} else if (arg === "--max-steps") {
|
|
227
|
+
result.maxSteps = parseInt(args[++i] ?? "25", 10);
|
|
228
|
+
} else if (arg.startsWith("--max-steps=")) {
|
|
229
|
+
result.maxSteps = parseInt(arg.slice("--max-steps=".length), 10);
|
|
230
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
231
|
+
printHelp();
|
|
232
|
+
process.exit(0);
|
|
233
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
234
|
+
console.log(CLI_VERSION.version);
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
i++;
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
function printHelp() {
|
|
242
|
+
console.log(`
|
|
243
|
+
OpenHarness CLI (oh)
|
|
244
|
+
|
|
245
|
+
Usage:
|
|
246
|
+
oh Start REPL (default)
|
|
247
|
+
oh repl Start REPL
|
|
248
|
+
oh run "<text>" Run a single turn
|
|
249
|
+
|
|
250
|
+
Options:
|
|
251
|
+
--workdir, -w <dir> Working directory (default: cwd)
|
|
252
|
+
--config, -c <file> Config file (default: harness.config.ts)
|
|
253
|
+
--agent, -a <name> Agent name (required if multiple agents)
|
|
254
|
+
--conversation <id> Conversation ID
|
|
255
|
+
--max-steps <n> Maximum steps per turn
|
|
256
|
+
--help, -h Show this help
|
|
257
|
+
--version, -v Show version
|
|
258
|
+
`);
|
|
259
|
+
}
|
|
260
|
+
async function main() {
|
|
261
|
+
const parsed = parseArgs(process.argv);
|
|
262
|
+
const opts = {
|
|
263
|
+
workdir: parsed.workdir,
|
|
264
|
+
config: parsed.config,
|
|
265
|
+
agent: parsed.agent,
|
|
266
|
+
conversation: parsed.conversation,
|
|
267
|
+
maxSteps: parsed.maxSteps
|
|
268
|
+
};
|
|
269
|
+
if (parsed.command === "run") {
|
|
270
|
+
if (!parsed.text) {
|
|
271
|
+
console.error('Usage: oh run "<text>"');
|
|
272
|
+
process.exit(2);
|
|
273
|
+
}
|
|
274
|
+
await runCommand(parsed.text, opts);
|
|
275
|
+
} else {
|
|
276
|
+
await replCommand(opts);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
main().catch((err) => {
|
|
280
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
281
|
+
process.exit(1);
|
|
282
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goondan/openharness-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oh": "./dist/index.js"
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"dotenv": "^16.4.0",
|
|
20
20
|
"jiti": "^2.6.1",
|
|
21
|
-
"@goondan/openharness
|
|
22
|
-
"@goondan/openharness": "0.1.
|
|
21
|
+
"@goondan/openharness": "0.1.10",
|
|
22
|
+
"@goondan/openharness-types": "0.1.10"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^22.0.0",
|