@baanish/hydra-cli 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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +338 -0
- package/dist/config.js.map +1 -0
- package/dist/db/client.d.ts +10 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +93 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/queries.d.ts +67 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +336 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/engine/concurrency.d.ts +3 -0
- package/dist/engine/concurrency.d.ts.map +1 -0
- package/dist/engine/concurrency.js +42 -0
- package/dist/engine/concurrency.js.map +1 -0
- package/dist/engine/eta.d.ts +16 -0
- package/dist/engine/eta.d.ts.map +1 -0
- package/dist/engine/eta.js +54 -0
- package/dist/engine/eta.js.map +1 -0
- package/dist/engine/model.d.ts +57 -0
- package/dist/engine/model.d.ts.map +1 -0
- package/dist/engine/model.js +445 -0
- package/dist/engine/model.js.map +1 -0
- package/dist/engine/personas.d.ts +30 -0
- package/dist/engine/personas.d.ts.map +1 -0
- package/dist/engine/personas.js +336 -0
- package/dist/engine/personas.js.map +1 -0
- package/dist/engine/pipeline.d.ts +61 -0
- package/dist/engine/pipeline.d.ts.map +1 -0
- package/dist/engine/pipeline.js +638 -0
- package/dist/engine/pipeline.js.map +1 -0
- package/dist/engine/prompts.d.ts +10 -0
- package/dist/engine/prompts.d.ts.map +1 -0
- package/dist/engine/prompts.js +49 -0
- package/dist/engine/prompts.js.map +1 -0
- package/dist/engine/search.d.ts +46 -0
- package/dist/engine/search.d.ts.map +1 -0
- package/dist/engine/search.js +159 -0
- package/dist/engine/search.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +648 -0
- package/dist/index.js.map +1 -0
- package/dist/security.d.ts +18 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +168 -0
- package/dist/security.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/agent-mode.d.ts +8 -0
- package/dist/ui/agent-mode.d.ts.map +1 -0
- package/dist/ui/agent-mode.js +138 -0
- package/dist/ui/agent-mode.js.map +1 -0
- package/dist/ui/animations.d.ts +8 -0
- package/dist/ui/animations.d.ts.map +1 -0
- package/dist/ui/animations.js +19 -0
- package/dist/ui/animations.js.map +1 -0
- package/dist/ui/components/agent-list.d.ts +2 -0
- package/dist/ui/components/agent-list.d.ts.map +1 -0
- package/dist/ui/components/agent-list.js +2 -0
- package/dist/ui/components/agent-list.js.map +1 -0
- package/dist/ui/components/header.d.ts +2 -0
- package/dist/ui/components/header.d.ts.map +1 -0
- package/dist/ui/components/header.js +2 -0
- package/dist/ui/components/header.js.map +1 -0
- package/dist/ui/components/phase-bar.d.ts +2 -0
- package/dist/ui/components/phase-bar.d.ts.map +1 -0
- package/dist/ui/components/phase-bar.js +2 -0
- package/dist/ui/components/phase-bar.js.map +1 -0
- package/dist/ui/components/stats-bar.d.ts +2 -0
- package/dist/ui/components/stats-bar.d.ts.map +1 -0
- package/dist/ui/components/stats-bar.js +2 -0
- package/dist/ui/components/stats-bar.js.map +1 -0
- package/dist/ui/tui.d.ts +18 -0
- package/dist/ui/tui.d.ts.map +1 -0
- package/dist/ui/tui.js +464 -0
- package/dist/ui/tui.js.map +1 -0
- package/dist/web/app.html +1352 -0
- package/dist/web/index.d.ts +2 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +2 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/server.d.ts +2 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +864 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aanish Bhirud
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# hydra-cli
|
|
2
|
+
|
|
3
|
+
hydra is a multi-agent research and synthesis cli that breaks a question into specialist workstreams, runs parallel research plus debate rounds, and produces one consolidated synthesis. it is built for long-form, evidence-oriented questions where a single-model answer is too shallow.
|
|
4
|
+
|
|
5
|
+
## requirements
|
|
6
|
+
|
|
7
|
+
- node `>=24`
|
|
8
|
+
- npm `>=11`
|
|
9
|
+
|
|
10
|
+
the `engines` field in `package.json` enforces `"node": ">=24"`. this migration is intentionally node-24-first: the cli now relies on modern node esm behavior, built-in web platform primitives like `fetch`/`Request`/`Response`, and the runtime shape we verified for the node http-to-fetch web adapter. older node versions are not tested in this phase, so treat them as unsupported until compatibility is widened deliberately.
|
|
11
|
+
|
|
12
|
+
## install and run
|
|
13
|
+
|
|
14
|
+
### one-off with npx
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @baanish/hydra-cli --help
|
|
18
|
+
npx @baanish/hydra-cli run "market entry strategy for industrial batteries"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### project-local install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @baanish/hydra-cli
|
|
25
|
+
npx hydra --help
|
|
26
|
+
npx hydra run "market entry strategy for industrial batteries"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### global install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g @baanish/hydra-cli
|
|
33
|
+
hydra --help
|
|
34
|
+
hydra run "market entry strategy for industrial batteries"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## quick start
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
hydra config set synthetic-api-key <key>
|
|
41
|
+
# optional llm override key:
|
|
42
|
+
# hydra config set api-key <key>
|
|
43
|
+
hydra run "your query"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
to quickly see a full real run output (the bundled ai transition 2036 retrospective) without running anything:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cat examples/ai-transition-2036.json | jq -r '.brief'
|
|
50
|
+
# no jq:
|
|
51
|
+
node --input-type=module -e "import { readFile } from 'node:fs/promises'; console.log(JSON.parse(await readFile('./examples/ai-transition-2036.json', 'utf8')).brief)"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## storage
|
|
55
|
+
|
|
56
|
+
hydra keeps the same on-disk locations across install modes:
|
|
57
|
+
|
|
58
|
+
- config: `~/.config/hydra-cli/config.json`
|
|
59
|
+
- database: `~/.config/hydra-cli/hydra.db`
|
|
60
|
+
- personas: `~/.config/hydra-cli/personas.json`
|
|
61
|
+
|
|
62
|
+
## key commands
|
|
63
|
+
|
|
64
|
+
| command | purpose | example |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| `hydra run <query>` | start a run (also supports `hydra "<query>"`) | `hydra run "market entry strategy"` |
|
|
67
|
+
| `hydra run --custom-personas-only <query>` | use custom personas only and fill any shortfall with ephemeral generated personas | `hydra run --custom-personas-only --agents 5 "supply chain strategy"` |
|
|
68
|
+
| `hydra view <run-id>` | inspect a run summary | `hydra view Rw9k...` |
|
|
69
|
+
| `hydra history` | list recent runs | `hydra history --limit 20` |
|
|
70
|
+
| `hydra config show` | print effective config with masked keys | `hydra config show` |
|
|
71
|
+
| `hydra config set <key> <value>` | update a config value | `hydra config set max-concurrency 5` |
|
|
72
|
+
| `hydra web` | launch the local web ui with an authenticated local api session | `hydra web --port 3737` |
|
|
73
|
+
|
|
74
|
+
## personas
|
|
75
|
+
|
|
76
|
+
personas are specialist analytical lenses assigned to agents (for example `skeptic`, `futurist`, `economist`) to bias how perspectives are generated and debated. hydra ships with 20 built-in personas.
|
|
77
|
+
|
|
78
|
+
| command | purpose | example |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| `hydra persona list` | list all personas (built-in + custom) | `hydra persona list` |
|
|
81
|
+
| `hydra persona list --json` | output personas as json | `hydra persona list --json` |
|
|
82
|
+
| `hydra persona add` | add a custom persona | `hydra persona add --name "the lawyer" --description "applies legal reasoning and precedent." --methodology "case law analysis"` |
|
|
83
|
+
| `hydra persona remove <id>` | remove a custom persona | `hydra persona remove the-lawyer` |
|
|
84
|
+
|
|
85
|
+
custom personas are stored in `~/.config/hydra-cli/personas.json`. built-in personas cannot be removed. if `--id` is not provided when adding a persona, it is auto-derived from the name using slugification.
|
|
86
|
+
|
|
87
|
+
## config options
|
|
88
|
+
|
|
89
|
+
| key | type | default |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| `apiKey` | `string \| undefined` | unset |
|
|
92
|
+
| `syntheticApiKey` | `string \| undefined` | unset |
|
|
93
|
+
| `searchProvider` | `"synthetic" \| "exa" \| "brave"` | `synthetic` |
|
|
94
|
+
| `exaApiKey` | `string \| undefined` | unset |
|
|
95
|
+
| `braveApiKey` | `string \| undefined` | unset |
|
|
96
|
+
| `baseUrl` | `string` | `https://api.synthetic.new/openai/v1` |
|
|
97
|
+
| `model` | `string` | `hf:MiniMaxAI/MiniMax-M2.5` |
|
|
98
|
+
| `defaultAgentCount` | `number` | `5` |
|
|
99
|
+
| `maxConcurrency` | `number` | `5` |
|
|
100
|
+
| `debateRounds` | `number` | `2` |
|
|
101
|
+
| `searchEnabled` | `boolean` | `true` |
|
|
102
|
+
| `customPersonasOnly` | `boolean` | `false` |
|
|
103
|
+
|
|
104
|
+
## development
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm install
|
|
108
|
+
npm run lint
|
|
109
|
+
npm run typecheck
|
|
110
|
+
npm test
|
|
111
|
+
npm run build
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
manual first publish:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm publish --access public
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## note on backend
|
|
121
|
+
|
|
122
|
+
hydra uses synthetic.new as the default openai-compatible llm backend (`baseUrl` + model defaults target synthetic.new). `baseUrl` must use `https://`, or `http://` only for localhost / loopback development endpoints, and embedded url credentials are rejected.
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HydraConfig, HydraConfigFile } from "./types.js";
|
|
2
|
+
/** default api base url for llm requests. */
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "https://api.synthetic.new/openai/v1";
|
|
4
|
+
/** default llm model identifier used by the project. */
|
|
5
|
+
export declare const DEFAULT_MODEL = "hf:MiniMaxAI/MiniMax-M2.5";
|
|
6
|
+
/** minimum supported debate rounds value. */
|
|
7
|
+
export declare const MIN_DEBATE_ROUNDS = 1;
|
|
8
|
+
/** maximum supported debate rounds value. */
|
|
9
|
+
export declare const MAX_DEBATE_ROUNDS = 8;
|
|
10
|
+
/** config directory under user home. */
|
|
11
|
+
export declare const CONFIG_DIR: string;
|
|
12
|
+
/** default config file path. */
|
|
13
|
+
export declare const CONFIG_FILE: string;
|
|
14
|
+
/** convert raw numeric values into clamped integers with safe fallback. */
|
|
15
|
+
export declare function clampInt(value: unknown, min: number, max: number, fallback: number): number;
|
|
16
|
+
/** return resolved config file path for diagnostics and tests. */
|
|
17
|
+
export declare function getConfigPath(): string;
|
|
18
|
+
/** load config from defaults, env, and overrides and return validated values. */
|
|
19
|
+
export declare function loadConfig(overrides?: HydraConfigFile): HydraConfig;
|
|
20
|
+
/** persist config values and return normalized merged config. */
|
|
21
|
+
export declare function writeConfig(updates: HydraConfigFile): HydraConfig;
|
|
22
|
+
/** mask api-like values for display to avoid accidental leakage in logs. */
|
|
23
|
+
export declare function maskConfigValue(value: string | undefined): string;
|
|
24
|
+
/** sanitize raw config-string input and return typed value or validation error. */
|
|
25
|
+
export declare function sanitizeConfigValueForSet(key: keyof HydraConfig, value: string): {
|
|
26
|
+
error?: string;
|
|
27
|
+
value: unknown;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAkB,MAAM,YAAY,CAAC;AAE/E,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,wCAAwC,CAAC;AACtE,wDAAwD;AACxD,eAAO,MAAM,aAAa,8BAA8B,CAAC;AACzD,6CAA6C;AAC7C,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,6CAA6C;AAC7C,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,wCAAwC;AACxC,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,gCAAgC;AAChC,eAAO,MAAM,WAAW,QAAqC,CAAC;AAiB9D,2EAA2E;AAC3E,wBAAgB,QAAQ,CACvB,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACd,MAAM,CAMR;AAoMD,kEAAkE;AAClE,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,SAAS,GAAE,eAAoB,GAAG,WAAW,CAQvE;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAgBjE;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAajE;AAED,mFAAmF;AACnF,wBAAgB,yBAAyB,CACxC,GAAG,EAAE,MAAM,WAAW,EACtB,KAAK,EAAE,MAAM,GACX;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAkHpC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { validateBaseUrl } from "./security.js";
|
|
5
|
+
/** default api base url for llm requests. */
|
|
6
|
+
export const DEFAULT_BASE_URL = "https://api.synthetic.new/openai/v1";
|
|
7
|
+
/** default llm model identifier used by the project. */
|
|
8
|
+
export const DEFAULT_MODEL = "hf:MiniMaxAI/MiniMax-M2.5";
|
|
9
|
+
/** minimum supported debate rounds value. */
|
|
10
|
+
export const MIN_DEBATE_ROUNDS = 1;
|
|
11
|
+
/** maximum supported debate rounds value. */
|
|
12
|
+
export const MAX_DEBATE_ROUNDS = 8;
|
|
13
|
+
/** config directory under user home. */
|
|
14
|
+
export const CONFIG_DIR = resolve(homedir(), ".config", "hydra-cli");
|
|
15
|
+
/** default config file path. */
|
|
16
|
+
export const CONFIG_FILE = resolve(CONFIG_DIR, "config.json");
|
|
17
|
+
const DEFAULTS = {
|
|
18
|
+
apiKey: undefined,
|
|
19
|
+
syntheticApiKey: undefined,
|
|
20
|
+
searchProvider: "synthetic",
|
|
21
|
+
exaApiKey: undefined,
|
|
22
|
+
braveApiKey: undefined,
|
|
23
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
24
|
+
model: DEFAULT_MODEL,
|
|
25
|
+
defaultAgentCount: 5,
|
|
26
|
+
maxConcurrency: 1,
|
|
27
|
+
debateRounds: 2,
|
|
28
|
+
searchEnabled: true,
|
|
29
|
+
customPersonasOnly: false,
|
|
30
|
+
};
|
|
31
|
+
/** convert raw numeric values into clamped integers with safe fallback. */
|
|
32
|
+
export function clampInt(value, min, max, fallback) {
|
|
33
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
34
|
+
if (!Number.isFinite(parsed)) {
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
return Math.min(Math.max(parsed, min), max);
|
|
38
|
+
}
|
|
39
|
+
/** normalize truthy / falsy string values from env and config files. */
|
|
40
|
+
function normalizeBoolean(value) {
|
|
41
|
+
if (typeof value === "boolean") {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
const normalized = value.trim().toLowerCase();
|
|
46
|
+
if (["1", "true", "yes", "y", "on"].includes(normalized)) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (["0", "false", "no", "n", "off"].includes(normalized)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** normalize and fallback invalid search provider values. */
|
|
56
|
+
function normalizeSearchProvider(value) {
|
|
57
|
+
if (typeof value === "string") {
|
|
58
|
+
const normalized = value.trim().toLowerCase();
|
|
59
|
+
if (normalized === "synthetic" ||
|
|
60
|
+
normalized === "exa" ||
|
|
61
|
+
normalized === "brave") {
|
|
62
|
+
return normalized;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return DEFAULTS.searchProvider;
|
|
66
|
+
}
|
|
67
|
+
/** trim optional string values and convert blank values to undefined. */
|
|
68
|
+
function trimOptionalString(value) {
|
|
69
|
+
if (typeof value !== "string") {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const trimmed = value.trim();
|
|
73
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
74
|
+
}
|
|
75
|
+
/** trim optional api key strings and convert blank values to undefined. */
|
|
76
|
+
function trimOptionalApiKey(value) {
|
|
77
|
+
return trimOptionalString(value);
|
|
78
|
+
}
|
|
79
|
+
/** normalize base-url values and surface invalid explicit settings immediately. */
|
|
80
|
+
function normalizeBaseUrl(value) {
|
|
81
|
+
if (typeof value !== "string") {
|
|
82
|
+
return DEFAULTS.baseUrl;
|
|
83
|
+
}
|
|
84
|
+
const trimmed = value.trim();
|
|
85
|
+
if (!trimmed) {
|
|
86
|
+
return DEFAULTS.baseUrl;
|
|
87
|
+
}
|
|
88
|
+
const parsed = validateBaseUrl(trimmed);
|
|
89
|
+
if (!parsed.value) {
|
|
90
|
+
throw new Error(`invalid base-url "${trimmed}": ${parsed.error ?? "must be a valid absolute URL"}`);
|
|
91
|
+
}
|
|
92
|
+
return parsed.value;
|
|
93
|
+
}
|
|
94
|
+
/** ensure the config directory exists before read/write operations. */
|
|
95
|
+
function ensureConfigDir() {
|
|
96
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
97
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** load raw config from disk and return parsed config json if valid. */
|
|
101
|
+
function readConfigFile() {
|
|
102
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
const raw = readFileSync(CONFIG_FILE, "utf8").trim();
|
|
106
|
+
if (!raw) {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(raw);
|
|
111
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
return parsed;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** overlay environment variables on top of persisted config values. */
|
|
121
|
+
function applyEnvironmentOverrides(config) {
|
|
122
|
+
const merged = { ...config };
|
|
123
|
+
if (process.env.HYDRA_API_KEY) {
|
|
124
|
+
merged.apiKey = process.env.HYDRA_API_KEY;
|
|
125
|
+
}
|
|
126
|
+
if (process.env.HYDRA_SYNTHETIC_API_KEY) {
|
|
127
|
+
merged.syntheticApiKey = process.env.HYDRA_SYNTHETIC_API_KEY;
|
|
128
|
+
}
|
|
129
|
+
else if (process.env.SYNTHETIC_API_KEY) {
|
|
130
|
+
merged.syntheticApiKey = process.env.SYNTHETIC_API_KEY;
|
|
131
|
+
}
|
|
132
|
+
if (process.env.HYDRA_SEARCH_PROVIDER) {
|
|
133
|
+
merged.searchProvider = normalizeSearchProvider(process.env.HYDRA_SEARCH_PROVIDER);
|
|
134
|
+
}
|
|
135
|
+
if (process.env.HYDRA_EXA_API_KEY) {
|
|
136
|
+
merged.exaApiKey = process.env.HYDRA_EXA_API_KEY;
|
|
137
|
+
}
|
|
138
|
+
else if (process.env.EXA_API_KEY) {
|
|
139
|
+
merged.exaApiKey = process.env.EXA_API_KEY;
|
|
140
|
+
}
|
|
141
|
+
if (process.env.HYDRA_BRAVE_API_KEY) {
|
|
142
|
+
merged.braveApiKey = process.env.HYDRA_BRAVE_API_KEY;
|
|
143
|
+
}
|
|
144
|
+
else if (process.env.BRAVE_API_KEY) {
|
|
145
|
+
merged.braveApiKey = process.env.BRAVE_API_KEY;
|
|
146
|
+
}
|
|
147
|
+
if (process.env.HYDRA_MODEL) {
|
|
148
|
+
merged.model = process.env.HYDRA_MODEL;
|
|
149
|
+
}
|
|
150
|
+
if (process.env.HYDRA_ORCHESTRATOR_MODEL !== undefined) {
|
|
151
|
+
merged.orchestratorModel = process.env.HYDRA_ORCHESTRATOR_MODEL;
|
|
152
|
+
}
|
|
153
|
+
if (process.env.HYDRA_RESEARCH_MODEL !== undefined) {
|
|
154
|
+
merged.researchModel = process.env.HYDRA_RESEARCH_MODEL;
|
|
155
|
+
}
|
|
156
|
+
if (process.env.HYDRA_BASE_URL) {
|
|
157
|
+
merged.baseUrl = process.env.HYDRA_BASE_URL;
|
|
158
|
+
}
|
|
159
|
+
if (process.env.HYDRA_CUSTOM_PERSONAS_ONLY) {
|
|
160
|
+
const parsed = normalizeBoolean(process.env.HYDRA_CUSTOM_PERSONAS_ONLY);
|
|
161
|
+
if (parsed !== undefined) {
|
|
162
|
+
merged.customPersonasOnly = parsed;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (process.env.HYDRA_CONCURRENCY) {
|
|
166
|
+
const parsed = Number.parseInt(process.env.HYDRA_CONCURRENCY, 10);
|
|
167
|
+
if (Number.isFinite(parsed)) {
|
|
168
|
+
merged.maxConcurrency = parsed;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return merged;
|
|
172
|
+
}
|
|
173
|
+
/** normalize config values and apply hard constraints to numeric fields. */
|
|
174
|
+
function normalizeConfig(config) {
|
|
175
|
+
const syntheticApiKey = trimOptionalApiKey(config.syntheticApiKey);
|
|
176
|
+
const explicitApiKey = trimOptionalApiKey(config.apiKey);
|
|
177
|
+
const apiKey = explicitApiKey ?? syntheticApiKey;
|
|
178
|
+
return {
|
|
179
|
+
apiKey: apiKey ?? undefined,
|
|
180
|
+
syntheticApiKey,
|
|
181
|
+
searchProvider: normalizeSearchProvider(config.searchProvider),
|
|
182
|
+
exaApiKey: trimOptionalApiKey(config.exaApiKey),
|
|
183
|
+
braveApiKey: trimOptionalApiKey(config.braveApiKey),
|
|
184
|
+
baseUrl: normalizeBaseUrl(config.baseUrl),
|
|
185
|
+
model: config.model || DEFAULTS.model,
|
|
186
|
+
orchestratorModel: trimOptionalString(config.orchestratorModel),
|
|
187
|
+
researchModel: trimOptionalString(config.researchModel),
|
|
188
|
+
defaultAgentCount: clampInt(config.defaultAgentCount, 1, 20, DEFAULTS.defaultAgentCount),
|
|
189
|
+
maxConcurrency: clampInt(config.maxConcurrency, 1, 1, DEFAULTS.maxConcurrency),
|
|
190
|
+
debateRounds: clampInt(config.debateRounds, MIN_DEBATE_ROUNDS, MAX_DEBATE_ROUNDS, DEFAULTS.debateRounds),
|
|
191
|
+
searchEnabled: typeof config.searchEnabled === "boolean"
|
|
192
|
+
? config.searchEnabled
|
|
193
|
+
: DEFAULTS.searchEnabled,
|
|
194
|
+
customPersonasOnly: typeof config.customPersonasOnly === "boolean"
|
|
195
|
+
? config.customPersonasOnly
|
|
196
|
+
: DEFAULTS.customPersonasOnly,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/** return resolved config file path for diagnostics and tests. */
|
|
200
|
+
export function getConfigPath() {
|
|
201
|
+
return CONFIG_FILE;
|
|
202
|
+
}
|
|
203
|
+
/** load config from defaults, env, and overrides and return validated values. */
|
|
204
|
+
export function loadConfig(overrides = {}) {
|
|
205
|
+
const merged = {
|
|
206
|
+
...DEFAULTS,
|
|
207
|
+
...applyEnvironmentOverrides(readConfigFile()),
|
|
208
|
+
...overrides,
|
|
209
|
+
};
|
|
210
|
+
return normalizeConfig(merged);
|
|
211
|
+
}
|
|
212
|
+
/** persist config values and return normalized merged config. */
|
|
213
|
+
export function writeConfig(updates) {
|
|
214
|
+
ensureConfigDir();
|
|
215
|
+
const normalized = normalizeConfig({
|
|
216
|
+
...DEFAULTS,
|
|
217
|
+
...readConfigFile(),
|
|
218
|
+
...updates,
|
|
219
|
+
});
|
|
220
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(normalized, null, 2), {
|
|
221
|
+
encoding: "utf8",
|
|
222
|
+
mode: 0o600,
|
|
223
|
+
});
|
|
224
|
+
// Explicit chmod ensures 0o600 regardless of umask.
|
|
225
|
+
chmodSync(CONFIG_FILE, 0o600);
|
|
226
|
+
return normalized;
|
|
227
|
+
}
|
|
228
|
+
/** mask api-like values for display to avoid accidental leakage in logs. */
|
|
229
|
+
export function maskConfigValue(value) {
|
|
230
|
+
if (!value) {
|
|
231
|
+
return "not set";
|
|
232
|
+
}
|
|
233
|
+
const trimmed = value.trim();
|
|
234
|
+
if (!trimmed) {
|
|
235
|
+
return "not set";
|
|
236
|
+
}
|
|
237
|
+
if (trimmed.length <= 8) {
|
|
238
|
+
return "[redacted]";
|
|
239
|
+
}
|
|
240
|
+
const suffix = trimmed.slice(-4);
|
|
241
|
+
return `sk-...${suffix}`;
|
|
242
|
+
}
|
|
243
|
+
/** sanitize raw config-string input and return typed value or validation error. */
|
|
244
|
+
export function sanitizeConfigValueForSet(key, value) {
|
|
245
|
+
if (key === "defaultAgentCount") {
|
|
246
|
+
const parsed = Number.parseInt(value, 10);
|
|
247
|
+
if (!Number.isFinite(parsed)) {
|
|
248
|
+
return {
|
|
249
|
+
error: "defaultAgentCount must be an integer",
|
|
250
|
+
value: undefined,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return { value: clampInt(parsed, 1, 20, DEFAULTS.defaultAgentCount) };
|
|
254
|
+
}
|
|
255
|
+
if (key === "maxConcurrency") {
|
|
256
|
+
const parsed = Number.parseInt(value, 10);
|
|
257
|
+
if (!Number.isFinite(parsed)) {
|
|
258
|
+
return {
|
|
259
|
+
error: "maxConcurrency must be 1",
|
|
260
|
+
value: undefined,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (parsed !== 1) {
|
|
264
|
+
return {
|
|
265
|
+
error: "maxConcurrency must be 1",
|
|
266
|
+
value: undefined,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return { value: parsed };
|
|
270
|
+
}
|
|
271
|
+
if (key === "debateRounds") {
|
|
272
|
+
const parsed = Number.parseInt(value, 10);
|
|
273
|
+
if (!Number.isFinite(parsed)) {
|
|
274
|
+
return { error: "debateRounds must be an integer", value: undefined };
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
value: clampInt(parsed, MIN_DEBATE_ROUNDS, MAX_DEBATE_ROUNDS, DEFAULTS.debateRounds),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if (key === "searchEnabled") {
|
|
281
|
+
const parsed = normalizeBoolean(value);
|
|
282
|
+
if (parsed === undefined) {
|
|
283
|
+
return {
|
|
284
|
+
error: "searchEnabled must be one of: true, false, 1, 0, yes, no",
|
|
285
|
+
value: undefined,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return { value: parsed };
|
|
289
|
+
}
|
|
290
|
+
if (key === "customPersonasOnly") {
|
|
291
|
+
const parsed = normalizeBoolean(value);
|
|
292
|
+
if (parsed === undefined) {
|
|
293
|
+
return {
|
|
294
|
+
error: "custom-personas-only must be one of: true, false, 1, 0, yes, no",
|
|
295
|
+
value: undefined,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return { value: parsed };
|
|
299
|
+
}
|
|
300
|
+
if (key === "baseUrl") {
|
|
301
|
+
const parsed = validateBaseUrl(value);
|
|
302
|
+
if (!parsed.value) {
|
|
303
|
+
return {
|
|
304
|
+
error: parsed.error ?? "base-url must be a valid absolute URL",
|
|
305
|
+
value: undefined,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return { value: parsed.value };
|
|
309
|
+
}
|
|
310
|
+
if (key === "model") {
|
|
311
|
+
return { value: value.trim() };
|
|
312
|
+
}
|
|
313
|
+
if (key === "syntheticApiKey" ||
|
|
314
|
+
key === "orchestratorModel" ||
|
|
315
|
+
key === "researchModel") {
|
|
316
|
+
return { value: trimOptionalString(value) };
|
|
317
|
+
}
|
|
318
|
+
if (key === "apiKey") {
|
|
319
|
+
return { value: trimOptionalString(value) };
|
|
320
|
+
}
|
|
321
|
+
if (key === "exaApiKey" || key === "braveApiKey") {
|
|
322
|
+
return { value: trimOptionalString(value) };
|
|
323
|
+
}
|
|
324
|
+
if (key === "searchProvider") {
|
|
325
|
+
const normalized = value.trim().toLowerCase();
|
|
326
|
+
if (normalized !== "synthetic" &&
|
|
327
|
+
normalized !== "exa" &&
|
|
328
|
+
normalized !== "brave") {
|
|
329
|
+
return {
|
|
330
|
+
error: "search-provider must be one of: synthetic, exa, brave",
|
|
331
|
+
value: undefined,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return { value: normalized };
|
|
335
|
+
}
|
|
336
|
+
return { value };
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
type SqliteDatabase = InstanceType<typeof Database>;
|
|
3
|
+
/** return fully qualified path to the sqlite database file. */
|
|
4
|
+
export declare function getDatabasePath(): string;
|
|
5
|
+
/** get initialized singleton sqlite connection with required pragmas applied. */
|
|
6
|
+
export declare function getDatabase(): SqliteDatabase;
|
|
7
|
+
/** close singleton database connection and clear in-memory handle. */
|
|
8
|
+
export declare function closeDatabase(): void;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAyCtC,KAAK,cAAc,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,CAAC;AAqCpD,+DAA+D;AAC/D,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,iFAAiF;AACjF,wBAAgB,WAAW,IAAI,cAAc,CAW5C;AAED,sEAAsE;AACtE,wBAAgB,aAAa,SAK5B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { CONFIG_DIR } from "../config.js";
|
|
5
|
+
const DB_DIR = CONFIG_DIR;
|
|
6
|
+
const DB_PATH = resolve(DB_DIR, "hydra.db");
|
|
7
|
+
const SCHEMA_SQL = `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS runs (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
query TEXT NOT NULL,
|
|
11
|
+
agent_count INTEGER NOT NULL,
|
|
12
|
+
status TEXT NOT NULL DEFAULT 'decomposing',
|
|
13
|
+
brief TEXT,
|
|
14
|
+
error TEXT,
|
|
15
|
+
pipeline_state TEXT,
|
|
16
|
+
total_prompt_tokens INTEGER DEFAULT 0,
|
|
17
|
+
total_completion_tokens INTEGER DEFAULT 0,
|
|
18
|
+
created_at INTEGER NOT NULL,
|
|
19
|
+
completed_at INTEGER,
|
|
20
|
+
elapsed_ms INTEGER
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE IF NOT EXISTS agent_runs (
|
|
24
|
+
id TEXT PRIMARY KEY,
|
|
25
|
+
run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
|
|
26
|
+
phase TEXT NOT NULL,
|
|
27
|
+
persona TEXT NOT NULL,
|
|
28
|
+
system_prompt TEXT NOT NULL,
|
|
29
|
+
messages TEXT,
|
|
30
|
+
output TEXT DEFAULT '',
|
|
31
|
+
search_queries TEXT,
|
|
32
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
33
|
+
prompt_tokens INTEGER DEFAULT 0,
|
|
34
|
+
completion_tokens INTEGER DEFAULT 0,
|
|
35
|
+
started_at INTEGER NOT NULL,
|
|
36
|
+
completed_at INTEGER
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_agent_runs_run_id ON agent_runs(run_id);
|
|
40
|
+
`;
|
|
41
|
+
let db = null;
|
|
42
|
+
function ensureDbDir() {
|
|
43
|
+
if (!existsSync(DB_DIR)) {
|
|
44
|
+
mkdirSync(DB_DIR, { recursive: true, mode: 0o700 });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function enforceDatabaseFilePermissions() {
|
|
48
|
+
const filePaths = [DB_PATH, `${DB_PATH}-wal`, `${DB_PATH}-shm`];
|
|
49
|
+
for (const path of filePaths) {
|
|
50
|
+
if (!existsSync(path)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
chmodSync(path, 0o600);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (path === DB_PATH) {
|
|
58
|
+
const message = error instanceof Error ? error.message : "unknown permission error";
|
|
59
|
+
console.warn(`[hydra] warning: could not set permissions on ${DB_PATH}: ${message}`);
|
|
60
|
+
}
|
|
61
|
+
// Ignore chmod failures for transient sqlite sidecar files.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function initializeSchema(database) {
|
|
66
|
+
database.exec("PRAGMA foreign_keys = ON;");
|
|
67
|
+
database.exec("PRAGMA journal_mode = WAL;");
|
|
68
|
+
database.exec(SCHEMA_SQL);
|
|
69
|
+
}
|
|
70
|
+
/** return fully qualified path to the sqlite database file. */
|
|
71
|
+
export function getDatabasePath() {
|
|
72
|
+
return DB_PATH;
|
|
73
|
+
}
|
|
74
|
+
/** get initialized singleton sqlite connection with required pragmas applied. */
|
|
75
|
+
export function getDatabase() {
|
|
76
|
+
if (db) {
|
|
77
|
+
return db;
|
|
78
|
+
}
|
|
79
|
+
ensureDbDir();
|
|
80
|
+
const database = new Database(DB_PATH);
|
|
81
|
+
initializeSchema(database);
|
|
82
|
+
enforceDatabaseFilePermissions();
|
|
83
|
+
db = database;
|
|
84
|
+
return db;
|
|
85
|
+
}
|
|
86
|
+
/** close singleton database connection and clear in-memory handle. */
|
|
87
|
+
export function closeDatabase() {
|
|
88
|
+
if (db) {
|
|
89
|
+
db.close();
|
|
90
|
+
db = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE5C,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiClB,CAAC;AAIF,IAAI,EAAE,GAA0B,IAAI,CAAC;AAErC,SAAS,WAAW;IACnB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;AACF,CAAC;AAED,SAAS,8BAA8B;IACtC,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,MAAM,EAAE,GAAG,OAAO,MAAM,CAAC,CAAC;IAChE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,SAAS;QACV,CAAC;QACD,IAAI,CAAC;YACJ,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACtB,MAAM,OAAO,GACZ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;gBACrE,OAAO,CAAC,IAAI,CACX,iDAAiD,OAAO,KAAK,OAAO,EAAE,CACtE,CAAC;YACH,CAAC;YACD,4DAA4D;QAC7D,CAAC;IACF,CAAC;AACF,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAwB;IACjD,QAAQ,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC3B,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,eAAe;IAC9B,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,WAAW;IAC1B,IAAI,EAAE,EAAE,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;IAED,WAAW,EAAE,CAAC;IACd,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,8BAA8B,EAAE,CAAC;IACjC,EAAE,GAAG,QAAQ,CAAC;IACd,OAAO,EAAE,CAAC;AACX,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,aAAa;IAC5B,IAAI,EAAE,EAAE,CAAC;QACR,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,GAAG,IAAI,CAAC;IACX,CAAC;AACF,CAAC"}
|