even-pf 0.5.0 → 0.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "even-pf",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "AI-assisted responsible grading tool for programming assignments",
5
5
  "module": "src/hosts/cli-host.ts",
6
6
  "type": "module",
@@ -32,11 +32,11 @@
32
32
  "zod-defaults": "^0.2.3"
33
33
  },
34
34
  "optionalDependencies": {
35
- "even-pf-linux-x64": "0.5.0",
36
- "even-pf-linux-arm64": "0.5.0",
37
- "even-pf-windows-x64": "0.5.0",
38
- "even-pf-darwin-x64": "0.5.0",
39
- "even-pf-darwin-arm64": "0.5.0"
35
+ "even-pf-linux-x64": "0.5.1",
36
+ "even-pf-linux-arm64": "0.5.1",
37
+ "even-pf-windows-x64": "0.5.1",
38
+ "even-pf-darwin-x64": "0.5.1",
39
+ "even-pf-darwin-arm64": "0.5.1"
40
40
  },
41
41
  "files": [
42
42
  "bin/even-pf.js",
@@ -11,6 +11,7 @@ export const COMMAND_NAMES: readonly string[] = [
11
11
  "clear",
12
12
  "status",
13
13
  "list",
14
+ "reload",
14
15
  "help",
15
16
  "exit",
16
17
  "quit",
@@ -21,6 +22,7 @@ const HELP_TEXT = `Available commands:
21
22
  clear [slug...] Clear output files (all if no slug given)
22
23
  status Show in-flight workflows and output file count
23
24
  list Show all configured workflow slugs
25
+ reload Reload config from the same source as startup
24
26
  help Show this help message
25
27
  exit / quit Shut down the program`;
26
28
 
@@ -48,6 +50,8 @@ export async function parseAndExecute(engine: Engine, rawInput: string): Promise
48
50
  return handleStatus(engine);
49
51
  case "list":
50
52
  return handleList(engine);
53
+ case "reload":
54
+ return await handleReload(engine);
51
55
  case "help":
52
56
  return { kind: "output", message: HELP_TEXT };
53
57
  case "exit":
@@ -137,3 +141,13 @@ function handleList(engine: Engine): CommandResult {
137
141
  }
138
142
  return { kind: "output", message: lines.join("\n") };
139
143
  }
144
+
145
+ async function handleReload(engine: Engine): Promise<CommandResult> {
146
+ try {
147
+ await engine.reloadConfig();
148
+ return { kind: "output", message: "Config reloaded successfully." };
149
+ } catch (err) {
150
+ const message = err instanceof Error ? err.message : String(err);
151
+ return { kind: "error", message: `Failed to reload config: ${message}` };
152
+ }
153
+ }
@@ -4,6 +4,7 @@ import { OutputViewer } from "../util/output-viewer.ts";
4
4
  import { executeAnalysisWorkflow } from "../workflow/analysis-workflow.ts";
5
5
  import { executeTestingWorkflow } from "../workflow/testing-workflow.ts";
6
6
  import type { WorkflowDependencies } from "../workflow/index.ts";
7
+ import { readConfig } from "../util/config.ts";
7
8
  import type { Config } from "../util/config.ts";
8
9
 
9
10
  export type WorkflowRunResult = {
@@ -26,8 +27,8 @@ export type EngineStatus = {
26
27
  */
27
28
  export class Engine {
28
29
  readonly outputViewer: OutputViewer;
29
- private readonly config: Config;
30
- private readonly deps: WorkflowDependencies;
30
+ private config: Config;
31
+ private deps: WorkflowDependencies;
31
32
  private readonly inFlightSlugs: Set<string> = new Set();
32
33
 
33
34
  constructor(config: Config) {
@@ -118,6 +119,23 @@ export class Engine {
118
119
  return results;
119
120
  }
120
121
 
122
+ /**
123
+ * Reload config from the same source as the initial run.
124
+ * Reinitializes LLM settings and workflow lists.
125
+ * Does not affect output_viewing settings or in-flight workflows.
126
+ */
127
+ async reloadConfig(): Promise<void> {
128
+ const newConfig = await readConfig();
129
+ this.config = newConfig;
130
+ this.deps = {
131
+ seed: Math.floor(Date.now() / 1000),
132
+ openRouter: new OpenRouter({
133
+ apiKey: newConfig.vendors.openrouter.api_key,
134
+ }),
135
+ outputViewer: this.outputViewer,
136
+ };
137
+ }
138
+
121
139
  /** Clear output files. If slugs provided, only clear matching filenames. */
122
140
  clearResults(slugFilter?: string[]): void {
123
141
  this.outputViewer.clearFiles(slugFilter);
@@ -94,6 +94,16 @@ function completer(line: string): [string[], string] {
94
94
  return [[], line];
95
95
  }
96
96
 
97
+ // --- Catch SDK-internal async errors that escape try/catch ---
98
+ // The OpenRouter SDK can throw network errors (e.g. ECONNRESET) from
99
+ // internally-spawned micro-tasks (e.g. response.text() inside matchFunc),
100
+ // after chat.send() has already been awaited. These surface as unhandled
101
+ // promise rejections and would crash Bun without this handler.
102
+ process.on("unhandledRejection", (reason: unknown) => {
103
+ const message = reason instanceof Error ? reason.message : String(reason);
104
+ console.error(chalk.red(`[epf] Unhandled async error (process kept alive): ${message}`));
105
+ });
106
+
97
107
  // --- REPL loop ---
98
108
  const rl = createInterface({
99
109
  input: process.stdin,
@@ -13,7 +13,7 @@ const configURLEnvVar = "EPF_CONFIG_URL";
13
13
 
14
14
  export type Config = z.infer<typeof ConfigSchema>;
15
15
 
16
- async function readConfig() {
16
+ export async function readConfig() {
17
17
  console.log(`Loading config`);
18
18
 
19
19
  let configFilePath: string;