@100xprompt/brain 0.0.0-stage → 0.1.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.
Files changed (260) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +235 -2
  3. package/package.json +48 -4
  4. package/src/adapters/aider/index.ts +8 -0
  5. package/src/adapters/aider/reader.ts +129 -0
  6. package/src/adapters/aider/records.ts +220 -0
  7. package/src/adapters/aider/writer.ts +269 -0
  8. package/src/adapters/amazon-q-cli/index.ts +8 -0
  9. package/src/adapters/amazon-q-cli/reader.ts +84 -0
  10. package/src/adapters/amazon-q-cli/records.ts +191 -0
  11. package/src/adapters/amazon-q-cli/writer.ts +370 -0
  12. package/src/adapters/amazon-q-ide/index.ts +9 -0
  13. package/src/adapters/amazon-q-ide/reader.ts +131 -0
  14. package/src/adapters/amazon-q-ide/records.ts +141 -0
  15. package/src/adapters/amazon-q-ide/writer.ts +325 -0
  16. package/src/adapters/antigravity/index.ts +9 -0
  17. package/src/adapters/antigravity/proto.ts +194 -0
  18. package/src/adapters/antigravity/reader.ts +424 -0
  19. package/src/adapters/antigravity/records.ts +236 -0
  20. package/src/adapters/antigravity/writer.ts +463 -0
  21. package/src/adapters/auggie/index.ts +36 -0
  22. package/src/adapters/claude-code/index.ts +8 -0
  23. package/src/adapters/claude-code/reader.ts +639 -0
  24. package/src/adapters/claude-code/records.ts +88 -0
  25. package/src/adapters/claude-code/writer.ts +329 -0
  26. package/src/adapters/cline/index.ts +12 -0
  27. package/src/adapters/cline/reader.ts +432 -0
  28. package/src/adapters/cline/records.ts +151 -0
  29. package/src/adapters/cline/vscdb.ts +56 -0
  30. package/src/adapters/cline/writer.ts +656 -0
  31. package/src/adapters/cline/xml.ts +177 -0
  32. package/src/adapters/cline4/index.ts +9 -0
  33. package/src/adapters/cline4/reader.ts +179 -0
  34. package/src/adapters/cline4/records.ts +92 -0
  35. package/src/adapters/cline4/writer.ts +361 -0
  36. package/src/adapters/codebuff/index.ts +8 -0
  37. package/src/adapters/codebuff/project.ts +207 -0
  38. package/src/adapters/codebuff/reader.ts +215 -0
  39. package/src/adapters/codebuff/records.ts +173 -0
  40. package/src/adapters/codebuff/writer.ts +383 -0
  41. package/src/adapters/codex/index.ts +8 -0
  42. package/src/adapters/codex/native.ts +239 -0
  43. package/src/adapters/codex/reader.ts +747 -0
  44. package/src/adapters/codex/records.ts +114 -0
  45. package/src/adapters/codex/writer.ts +479 -0
  46. package/src/adapters/continue/index.ts +9 -0
  47. package/src/adapters/continue/reader.ts +68 -0
  48. package/src/adapters/continue/records.ts +418 -0
  49. package/src/adapters/continue/writer.ts +162 -0
  50. package/src/adapters/copilot-cli/index.ts +9 -0
  51. package/src/adapters/copilot-cli/reader.ts +186 -0
  52. package/src/adapters/copilot-cli/records.ts +241 -0
  53. package/src/adapters/copilot-cli/writer.ts +353 -0
  54. package/src/adapters/crush/index.ts +8 -0
  55. package/src/adapters/crush/reader.ts +180 -0
  56. package/src/adapters/crush/records.ts +169 -0
  57. package/src/adapters/crush/writer.ts +333 -0
  58. package/src/adapters/cursor-cli/index.ts +405 -0
  59. package/src/adapters/cursor-ide/agentkv.ts +95 -0
  60. package/src/adapters/cursor-ide/index.ts +501 -0
  61. package/src/adapters/cursor-ide/transcripts.ts +122 -0
  62. package/src/adapters/cursor-ide/workspaces.ts +119 -0
  63. package/src/adapters/cursor-ide/writer.ts +170 -0
  64. package/src/adapters/devin/index.ts +8 -0
  65. package/src/adapters/devin/reader.ts +305 -0
  66. package/src/adapters/devin/records.ts +277 -0
  67. package/src/adapters/devin/writer.ts +523 -0
  68. package/src/adapters/factory-droid/index.ts +12 -0
  69. package/src/adapters/factory-droid/reader.ts +184 -0
  70. package/src/adapters/factory-droid/records.ts +79 -0
  71. package/src/adapters/factory-droid/writer.ts +350 -0
  72. package/src/adapters/gemini-cli/exchanges.ts +228 -0
  73. package/src/adapters/gemini-cli/index.ts +396 -0
  74. package/src/adapters/gemini-cli/records.ts +521 -0
  75. package/src/adapters/gemini-cli/writer.ts +354 -0
  76. package/src/adapters/goose/index.ts +8 -0
  77. package/src/adapters/goose/reader.ts +138 -0
  78. package/src/adapters/goose/records.ts +199 -0
  79. package/src/adapters/goose/writer.ts +340 -0
  80. package/src/adapters/grok-cli/image.ts +44 -0
  81. package/src/adapters/grok-cli/index.ts +8 -0
  82. package/src/adapters/grok-cli/reader.ts +302 -0
  83. package/src/adapters/grok-cli/records.ts +172 -0
  84. package/src/adapters/grok-cli/writer.ts +329 -0
  85. package/src/adapters/hermes/index.ts +8 -0
  86. package/src/adapters/hermes/reader.ts +249 -0
  87. package/src/adapters/hermes/records.ts +285 -0
  88. package/src/adapters/hermes/writer.ts +452 -0
  89. package/src/adapters/iflow-cli/index.ts +72 -0
  90. package/src/adapters/iflow-cli/records.ts +215 -0
  91. package/src/adapters/iflow-cli/writer.ts +204 -0
  92. package/src/adapters/index.ts +112 -0
  93. package/src/adapters/kilo-cli/index.ts +21 -0
  94. package/src/adapters/kilo-code/index.ts +11 -0
  95. package/src/adapters/kilo-code/writer.ts +92 -0
  96. package/src/adapters/kimi/index.ts +9 -0
  97. package/src/adapters/kimi/reader.ts +92 -0
  98. package/src/adapters/kimi/records.ts +221 -0
  99. package/src/adapters/kimi/writer.ts +367 -0
  100. package/src/adapters/kiro-cli/index.ts +8 -0
  101. package/src/adapters/kiro-cli/reader.ts +87 -0
  102. package/src/adapters/kiro-cli/records.ts +260 -0
  103. package/src/adapters/kiro-cli/writer.ts +305 -0
  104. package/src/adapters/kiro-ide/index.ts +8 -0
  105. package/src/adapters/kiro-ide/writer.ts +52 -0
  106. package/src/adapters/letta/index.ts +8 -0
  107. package/src/adapters/letta/reader.ts +177 -0
  108. package/src/adapters/letta/records.ts +119 -0
  109. package/src/adapters/letta/writer.ts +413 -0
  110. package/src/adapters/mastracode/index.ts +8 -0
  111. package/src/adapters/mastracode/reader.ts +323 -0
  112. package/src/adapters/mastracode/records.ts +336 -0
  113. package/src/adapters/mastracode/writer.ts +385 -0
  114. package/src/adapters/mistral-vibe/index.ts +9 -0
  115. package/src/adapters/mistral-vibe/reader.ts +62 -0
  116. package/src/adapters/mistral-vibe/records.ts +104 -0
  117. package/src/adapters/mistral-vibe/writer.ts +359 -0
  118. package/src/adapters/muse/index.ts +10 -0
  119. package/src/adapters/muse/reader.ts +332 -0
  120. package/src/adapters/muse/records.ts +104 -0
  121. package/src/adapters/muse/writer.ts +387 -0
  122. package/src/adapters/omp/index.ts +13 -0
  123. package/src/adapters/omp/reader.ts +314 -0
  124. package/src/adapters/omp/records.ts +153 -0
  125. package/src/adapters/omp/writer.ts +216 -0
  126. package/src/adapters/opencode/ids.ts +52 -0
  127. package/src/adapters/opencode/index.ts +31 -0
  128. package/src/adapters/opencode/note.ts +8 -0
  129. package/src/adapters/opencode/reader.ts +452 -0
  130. package/src/adapters/opencode/tools.ts +174 -0
  131. package/src/adapters/opencode/v2.ts +367 -0
  132. package/src/adapters/opencode/writer.ts +379 -0
  133. package/src/adapters/openhands/index.ts +8 -0
  134. package/src/adapters/openhands/reader.ts +222 -0
  135. package/src/adapters/openhands/records.ts +136 -0
  136. package/src/adapters/openhands/writer.ts +404 -0
  137. package/src/adapters/pearai/index.ts +10 -0
  138. package/src/adapters/pi/index.ts +28 -0
  139. package/src/adapters/pi/reader.ts +261 -0
  140. package/src/adapters/pi/records.ts +99 -0
  141. package/src/adapters/pi/writer.ts +362 -0
  142. package/src/adapters/qoder-cli/index.ts +8 -0
  143. package/src/adapters/qoder-cli/reader.ts +83 -0
  144. package/src/adapters/qoder-cli/records.ts +45 -0
  145. package/src/adapters/qoder-cli/writer.ts +72 -0
  146. package/src/adapters/qwen-code/index.ts +84 -0
  147. package/src/adapters/qwen-code/records.ts +194 -0
  148. package/src/adapters/qwen-code/writer.ts +224 -0
  149. package/src/adapters/tabnine-cli/index.ts +8 -0
  150. package/src/adapters/tabnine-cli/reader.ts +106 -0
  151. package/src/adapters/tabnine-cli/records.ts +33 -0
  152. package/src/adapters/tabnine-cli/writer.ts +156 -0
  153. package/src/adapters/types.ts +73 -0
  154. package/src/adapters/void/index.ts +9 -0
  155. package/src/adapters/void/reader.ts +48 -0
  156. package/src/adapters/void/records.ts +285 -0
  157. package/src/adapters/void/writer.ts +93 -0
  158. package/src/adapters/vscode-copilot/index.ts +9 -0
  159. package/src/adapters/vscode-copilot/reader.ts +212 -0
  160. package/src/adapters/vscode-copilot/records.ts +388 -0
  161. package/src/adapters/vscode-copilot/shared.ts +68 -0
  162. package/src/adapters/vscode-copilot/writer.ts +546 -0
  163. package/src/adapters/zed/index.ts +8 -0
  164. package/src/adapters/zed/reader.ts +198 -0
  165. package/src/adapters/zed/records.ts +87 -0
  166. package/src/adapters/zed/writer.ts +341 -0
  167. package/src/brief-writers.ts +71 -0
  168. package/src/brief.ts +238 -0
  169. package/src/cli.ts +351 -0
  170. package/src/dashboard/cli.ts +148 -0
  171. package/src/dashboard/conversions.ts +170 -0
  172. package/src/dashboard/report.ts +261 -0
  173. package/src/dashboard/send.ts +213 -0
  174. package/src/dashboard/state.ts +95 -0
  175. package/src/fsutil.ts +133 -0
  176. package/src/gateway/common.ts +232 -0
  177. package/src/gateway/connect.ts +847 -0
  178. package/src/gateway/connectors/aider.ts +356 -0
  179. package/src/gateway/connectors/amazon-q-cli.ts +6 -0
  180. package/src/gateway/connectors/amazon-q-ide.ts +6 -0
  181. package/src/gateway/connectors/antigravity.ts +6 -0
  182. package/src/gateway/connectors/auggie.ts +6 -0
  183. package/src/gateway/connectors/cline-sdk.ts +85 -0
  184. package/src/gateway/connectors/cline.ts +34 -0
  185. package/src/gateway/connectors/codebuff.ts +6 -0
  186. package/src/gateway/connectors/continue.ts +579 -0
  187. package/src/gateway/connectors/copilot-cli.ts +138 -0
  188. package/src/gateway/connectors/crush.ts +313 -0
  189. package/src/gateway/connectors/cursor-cli.ts +6 -0
  190. package/src/gateway/connectors/cursor.ts +6 -0
  191. package/src/gateway/connectors/devin.ts +6 -0
  192. package/src/gateway/connectors/factory-droid.ts +308 -0
  193. package/src/gateway/connectors/goose.ts +79 -0
  194. package/src/gateway/connectors/grok-cli.ts +82 -0
  195. package/src/gateway/connectors/hermes.ts +230 -0
  196. package/src/gateway/connectors/iflow-cli.ts +50 -0
  197. package/src/gateway/connectors/index.ts +86 -0
  198. package/src/gateway/connectors/kilo-cli.ts +434 -0
  199. package/src/gateway/connectors/kilo-code.ts +32 -0
  200. package/src/gateway/connectors/kimi-code.ts +241 -0
  201. package/src/gateway/connectors/kiro-cli.ts +6 -0
  202. package/src/gateway/connectors/kiro-ide.ts +6 -0
  203. package/src/gateway/connectors/letta-code.ts +170 -0
  204. package/src/gateway/connectors/mastracode.ts +91 -0
  205. package/src/gateway/connectors/mistral-vibe.ts +495 -0
  206. package/src/gateway/connectors/muse.ts +27 -0
  207. package/src/gateway/connectors/omp.ts +279 -0
  208. package/src/gateway/connectors/openhands.ts +296 -0
  209. package/src/gateway/connectors/pearai.ts +52 -0
  210. package/src/gateway/connectors/pi.ts +357 -0
  211. package/src/gateway/connectors/qoder-cli.ts +6 -0
  212. package/src/gateway/connectors/qwen-code.ts +411 -0
  213. package/src/gateway/connectors/roo.ts +43 -0
  214. package/src/gateway/connectors/tabnine-cli.ts +6 -0
  215. package/src/gateway/connectors/void.ts +6 -0
  216. package/src/gateway/connectors/vscode-copilot.ts +286 -0
  217. package/src/gateway/connectors/windsurf.ts +6 -0
  218. package/src/gateway/connectors/zed.ts +29 -0
  219. package/src/gateway/signin.ts +323 -0
  220. package/src/handoff.ts +104 -0
  221. package/src/hooks.ts +455 -0
  222. package/src/memory/audit.ts +514 -0
  223. package/src/memory/blobs.ts +94 -0
  224. package/src/memory/cli.ts +117 -0
  225. package/src/memory/distill.ts +381 -0
  226. package/src/memory/externalize.ts +435 -0
  227. package/src/memory/lookups.ts +70 -0
  228. package/src/memory/mcp-install.ts +401 -0
  229. package/src/memory/mcp-server.ts +296 -0
  230. package/src/memory/prepare.ts +179 -0
  231. package/src/memory/preview.ts +154 -0
  232. package/src/memory/project.ts +35 -0
  233. package/src/memory/redact.ts +72 -0
  234. package/src/memory/relevance.ts +187 -0
  235. package/src/memory/repomap.ts +516 -0
  236. package/src/memory/store.ts +270 -0
  237. package/src/memory/sync.ts +166 -0
  238. package/src/memory/tidy.ts +193 -0
  239. package/src/memory/types.ts +118 -0
  240. package/src/org/admin-ops.ts +96 -0
  241. package/src/org/auth.ts +112 -0
  242. package/src/org/backup.ts +115 -0
  243. package/src/org/cli.ts +438 -0
  244. package/src/org/client.ts +404 -0
  245. package/src/org/config.ts +44 -0
  246. package/src/org/crypto.ts +537 -0
  247. package/src/org/db.ts +435 -0
  248. package/src/org/graph.ts +318 -0
  249. package/src/org/oidc.ts +435 -0
  250. package/src/org/policy.ts +398 -0
  251. package/src/org/server.ts +920 -0
  252. package/src/org/sso.ts +316 -0
  253. package/src/org/tls.ts +66 -0
  254. package/src/org/types.ts +193 -0
  255. package/src/self.ts +68 -0
  256. package/src/session/attachments.ts +53 -0
  257. package/src/session/peekcache.ts +50 -0
  258. package/src/session/types.ts +104 -0
  259. package/src/session/util.ts +130 -0
  260. package/src/sqlite.ts +48 -0
@@ -0,0 +1,230 @@
1
+ // Gateway connector for Hermes Agent (Nous Research): a named provider "brain-gateway" (OpenAI Chat Completions) in
2
+ // ~/.hermes/config.yaml, made the default, with the key in ~/.hermes/.env.
3
+ //
4
+ // Checked against hermes-agent 0.18.2 (pyproject.toml; git caf557be, 2026-07-10), the installed copy under
5
+ // ~/.hermes/hermes-agent (read only), and its docs (website/docs/integrations/providers.md, "Named Custom Providers"):
6
+ // - config.yaml `providers:` is the keyed (v12+) schema for named endpoints: {name, base_url | api | url, api_key,
7
+ // key_env, api_mode | transport, default_model, extra_headers, ...} (hermes_cli/config.py
8
+ // _normalize_custom_provider_entry). The older `custom_providers:` list still works; `providers:` is a mapping, so one
9
+ // entry can be added and removed by its name without touching the person's others.
10
+ // - `model.provider: custom:<name>` selects it (hermes_cli/runtime_provider.py _get_named_custom_provider; the same
11
+ // value `hermes model` writes, hermes_cli/model_setup_flows.py), and `model.default` is the model id sent (it wins
12
+ // over `model.model` / `model.name`, config.py _normalize_root_model_keys). An explicit base URL or key only comes
13
+ // from `hermes --base-url/--api-key`, so the person's own model.base_url / model.api_key do not reach this provider.
14
+ // - The key: `key_env` names an environment variable (runtime_provider.py _resolve_named_custom_runtime), and
15
+ // ~/.hermes/.env is loaded into the environment at start, over the shell's own values (hermes_cli/env_loader.py
16
+ // load_hermes_dotenv). Hermes has no key-file or key-command option (its only external secret sources are Bitwarden
17
+ // and 1Password), so the key is copied into .env (BRAIN_GATEWAY_API_KEY, made 0600), the file Hermes keeps keys in;
18
+ // config.yaml holds no secret.
19
+ // - `extra_headers` on the provider entry is sent with every request on the OpenAI wire (runtime_provider.py
20
+ // _lift_extra_headers), so the context-edit header can go.
21
+ // - The gateway's /v1/models answers 403; Hermes then takes the context window from its catalog ("gemini" → 1M,
22
+ // agent/model_metadata.py), so nothing else needs setting.
23
+ // - $HERMES_HOME replaces ~/.hermes when set (a Hermes profile is its own HERMES_HOME).
24
+ // - Checked for real (scripts/gateway-real/hermes.ts): `hermes chat -q` sends its request to <route>/v1/chat/completions
25
+ // with Authorization: Bearer <key from .env>, the context-edit header and the gateway's model, even with the person's
26
+ // own model.base_url still pointing at OpenRouter.
27
+ // config.yaml is YAML with comments; the codec below edits `<section>/<entry>` lines in place and leaves every other line
28
+ // as it was.
29
+ import { join } from "node:path"
30
+ import { type Codec, type Connector, dirFromEnv, EDIT_HEADER, type Env, type Json, readKey, routeUrl, yamlTopLevel } from "../common.ts"
31
+
32
+ export const HERMES_PROVIDER = "brain-gateway"
33
+ export const HERMES_KEY_VAR = "BRAIN_GATEWAY_API_KEY"
34
+
35
+ // ---- YAML: one entry inside a top-level mapping -------------------------------------------------------------------------
36
+
37
+ const blank = (l: string) => l.trim() === "" || /^\s*#/.test(l)
38
+ const indentOf = (l: string) => /^[ \t]*/.exec(l)![0]
39
+ const esc = (k: string) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
40
+ const keyLine = (indent: string, k: string) => new RegExp(`^${esc(indent)}(?:${esc(k)}|"${esc(k)}"|'${esc(k)}')\\s*:(?:\\s|$)`)
41
+
42
+ /** YAML 1.1 (PyYAML, which Hermes uses) reads these plain words as booleans or null. */
43
+ const YAML11 = /^(?:y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF|null|Null|NULL|~)$/
44
+ const plainOk = (s: string) => /^[A-Za-z_][A-Za-z0-9_./:+-]*$/.test(s) && !YAML11.test(s) && !s.endsWith(":")
45
+ const yamlKey = (k: string) => (/^[A-Za-z_][A-Za-z0-9_-]*$/.test(k) && !YAML11.test(k) ? k : JSON.stringify(k))
46
+
47
+ /** A scalar written in `style` (the quoting the line had before), so a restored value comes back byte for byte. */
48
+ function scalar(v: unknown, style: string): string {
49
+ if (typeof v !== "string") return v === null ? "null" : typeof v === "object" ? JSON.stringify(v) : String(v)
50
+ if (style === "'" && !/[\n\\]/.test(v)) return `'${v.replace(/'/g, "''")}'`
51
+ if (style === '"' || !plainOk(v)) return JSON.stringify(v)
52
+ return v
53
+ }
54
+
55
+ /** The ` # comment` after a one-line `key: value`, with its leading spaces ("" when there is none). */
56
+ function comment(line: string): string {
57
+ let quote: string | undefined
58
+ for (let i = line.indexOf(":") + 1; i > 0 && i < line.length; i++) {
59
+ const c = line[i]!
60
+ if (quote) {
61
+ if (c === "\\" && quote === '"') i++
62
+ else if (c === quote) quote = undefined
63
+ } else if (c === '"' || c === "'") quote = c
64
+ else if (c === "#" && /[ \t]/.test(line[i - 1]!)) {
65
+ let j = i
66
+ while (j > 0 && /[ \t]/.test(line[j - 1]!)) j--
67
+ return line.slice(j).trimEnd()
68
+ }
69
+ }
70
+ return ""
71
+ }
72
+
73
+ /** What follows `key:` on its line, without the comment. */
74
+ const inlineValue = (line: string) => line.slice(line.indexOf(":") + 1, line.length - comment(line).length).trim()
75
+
76
+ /** Lines for `key: value` at `indent`: a mapping as an indented block, anything else on one line. */
77
+ function entryLines(indent: string, k: string, v: unknown, style = ""): string[] {
78
+ if (v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length)
79
+ return [`${indent}${yamlKey(k)}:`, ...Object.entries(v as Json).flatMap(([ck, cv]) => entryLines(`${indent} `, ck, cv))]
80
+ return [`${indent}${yamlKey(k)}: ${scalar(v, style)}`]
81
+ }
82
+
83
+ /** The end (exclusive) of the block that starts at line `at`: its more-indented lines, not counting trailing blanks/comments. */
84
+ function blockEnd(lines: string[], at: number): number {
85
+ const base = indentOf(lines[at]!).length
86
+ let end = at + 1
87
+ while (end < lines.length && (blank(lines[end]!) || indentOf(lines[end]!).length > base || (base === 0 && /^-\s/.test(lines[end]!)))) end++
88
+ while (end > at + 1 && blank(lines[end - 1]!)) end--
89
+ return end
90
+ }
91
+
92
+ interface Section {
93
+ at: number
94
+ end: number
95
+ /** The children's indentation ("" when it has none yet). */
96
+ indent: string
97
+ children: Array<{ key: string; at: number; end: number }>
98
+ }
99
+
100
+ /** The top-level mapping `name` as lines (undefined when absent). Throws when it is not a block mapping (or empty). */
101
+ function section(lines: string[], name: string): Section | undefined {
102
+ const at = lines.findIndex((l) => keyLine("", name).test(l))
103
+ if (at < 0) return undefined
104
+ const inline = inlineValue(lines[at]!)
105
+ if (inline && !/^(?:\{\s*\}|null|~)$/.test(inline)) throw new Error(`${name}: is written on one line (${inline.slice(0, 40)}), not as a section`)
106
+ const end = blockEnd(lines, at)
107
+ const first = lines.slice(at + 1, end).find((l) => !blank(l))
108
+ const indent = first ? indentOf(first) : ""
109
+ const children: Section["children"] = []
110
+ if (inline && first) throw new Error(`${name}: has both a value and a block`)
111
+ for (let i = at + 1; i < end; i++) {
112
+ const l = lines[i]!
113
+ if (blank(l) || indentOf(l) !== indent) continue
114
+ if (/^[ \t]*-(?:\s|$)/.test(l)) throw new Error(`${name}: is a list, not a section`)
115
+ const m = /^[ \t]*(?:"((?:[^"\\]|\\.)*)"|'((?:[^']|'')*)'|([^\s#'"][^:#]*?))\s*:(?:\s|$)/.exec(l)
116
+ if (!m) throw new Error(`${name}: holds something other than key: value entries`)
117
+ const key = m[1] !== undefined ? JSON.parse(`"${m[1]}"`) : m[2] !== undefined ? m[2].replace(/''/g, "'") : m[3]!
118
+ children.push({ key, at: i, end: blockEnd(lines, i) })
119
+ }
120
+ return { at, end, indent, children }
121
+ }
122
+
123
+ /**
124
+ * Keys are `<section>/<entry>` (model/provider, providers/brain-gateway): one entry inside a top-level mapping, set on one
125
+ * line (a mapping value as an indented block) and removed with its block. A section the removal leaves with nothing in it
126
+ * goes too, so connect-then-disconnect gives back the exact bytes (a `providers: {}` that only ever held this entry comes
127
+ * back as no `providers` at all, which Hermes reads the same way). Everything else, comments included, stays as it was.
128
+ */
129
+ export const hermesConfig: Codec = {
130
+ check(text) {
131
+ const why = yamlTopLevel.check!(text)
132
+ if (why) return why
133
+ if (!text.trim()) return undefined
134
+ const lines = text.replace(/\n$/, "").split("\n")
135
+ const parsed = (Bun.YAML.parse(text) as Json | null) ?? {}
136
+ for (const name of ["model", "providers"]) {
137
+ const v = parsed[name]
138
+ if (v !== undefined && v !== null && (typeof v !== "object" || Array.isArray(v))) return `${name}: is not a section of key: value entries; left as it is`
139
+ try {
140
+ section(lines, name)
141
+ } catch (e) {
142
+ return `${(e as Error).message}; left as it is`
143
+ }
144
+ }
145
+ return undefined
146
+ },
147
+ get(text, key) {
148
+ if (!text.trim()) return undefined
149
+ const [name, child] = key.split("/") as [string, string]
150
+ const v = (Bun.YAML.parse(text) as Json | null)?.[name]
151
+ return v && typeof v === "object" && !Array.isArray(v) ? (v as Json)[child] : undefined
152
+ },
153
+ set(text, key, value) {
154
+ const [name, child] = key.split("/") as [string, string]
155
+ const nl = text === "" || text.endsWith("\n")
156
+ const lines = text ? text.replace(/\n$/, "").split("\n") : []
157
+ const out = () => (lines.length ? lines.join("\n") + (nl ? "\n" : "") : "")
158
+ const sec = section(lines, name)
159
+ if (!sec) {
160
+ if (value === undefined) return text
161
+ lines.push(`${yamlKey(name)}:`, ...entryLines(" ", child, value))
162
+ return out()
163
+ }
164
+ const found = sec.children.find((c) => c.key === child)
165
+ if (value === undefined) {
166
+ if (!found) return text
167
+ lines.splice(found.at, found.end - found.at)
168
+ // A section left with nothing under it (as when connect added it) goes with its last entry; one that still holds
169
+ // indented lines (a comment) stays.
170
+ const next = lines[sec.at + 1]
171
+ if (!inlineValue(lines[sec.at]!) && (next === undefined || !/^[ \t]+\S/.test(next))) lines.splice(sec.at, 1)
172
+ return out()
173
+ }
174
+ if (found) {
175
+ const old = lines[found.at]!
176
+ const style = inlineValue(old)[0] ?? ""
177
+ const next = entryLines(sec.indent, child, value, style === '"' || style === "'" ? style : "")
178
+ // A one-line entry keeps its trailing comment.
179
+ if (found.end === found.at + 1 && next.length === 1) next[0] += comment(old)
180
+ lines.splice(found.at, found.end - found.at, ...next)
181
+ return out()
182
+ }
183
+ // A new entry goes after the section's last one; an empty `name: {}` (or null) becomes a block holding it.
184
+ if (inlineValue(lines[sec.at]!)) lines[sec.at] = `${yamlKey(name)}:${comment(lines[sec.at]!)}`
185
+ lines.splice(sec.end, 0, ...entryLines(sec.indent || " ", child, value))
186
+ return out()
187
+ },
188
+ }
189
+
190
+ const hermesDir = (home: string, env: Env) => dirFromEnv(env, "HERMES_HOME", join(home, ".hermes"))
191
+
192
+ export const connectors: Connector[] = [
193
+ {
194
+ id: "hermes",
195
+ label: "Hermes Agent",
196
+ file: (home, env) => join(hermesDir(home, env), "config.yaml"),
197
+ probe: [".hermes"],
198
+ authHeader: "authorization",
199
+ edits: (home, s, env) => [
200
+ {
201
+ path: join(hermesDir(home, env), "config.yaml"),
202
+ format: "custom",
203
+ codec: hermesConfig,
204
+ values: {
205
+ [`providers/${HERMES_PROVIDER}`]: {
206
+ name: "100xprompt gateway",
207
+ base_url: `${routeUrl(s)}/v1`,
208
+ key_env: HERMES_KEY_VAR,
209
+ api_mode: "chat_completions",
210
+ default_model: s.model,
211
+ ...(s.contextEdit ? { extra_headers: { [EDIT_HEADER]: "on" } } : {}),
212
+ },
213
+ "model/provider": `custom:${HERMES_PROVIDER}`,
214
+ "model/default": s.model,
215
+ },
216
+ },
217
+ {
218
+ path: join(hermesDir(home, env), ".env"),
219
+ format: "dotenv",
220
+ holdsKey: true,
221
+ secretKeys: [HERMES_KEY_VAR],
222
+ values: { [HERMES_KEY_VAR]: readKey(home) },
223
+ },
224
+ ],
225
+ notes: [
226
+ "Hermes now starts on the gateway's model (the provider \"100xprompt gateway\" in ~/.hermes/config.yaml, made the default); your own providers stay in its /model picker.",
227
+ `Hermes has no way to read a key from a file, so the key is kept in ~/.hermes/.env (${HERMES_KEY_VAR}), which is now readable by you only (0600).`,
228
+ ],
229
+ },
230
+ ]
@@ -0,0 +1,50 @@
1
+ // Gateway connector for iFlow CLI (`iflow`).
2
+ //
3
+ // Checked against iFlow CLI 0.5.19 as installed (~/.cache/brain-wire/iflow-cli, @iflow-ai/iflow-cli, bundle/iflow.js;
4
+ // minified, so places are named by line and function) and its docs (https://platform.iflow.cn/en/cli/configuration/settings):
5
+ // - User settings are ~/.iflow/settings.json under os.homedir(), whatever IFLOW_HOME says (iflow.js:5165, `eJ`, loaded by
6
+ // `hu`), read through strip-json-comments then JSON.parse (comments fine, no trailing commas), with $VAR / ${VAR}
7
+ // replaced from the environment. When iFlow CLI saves a setting itself it rewrites the whole file as plain JSON
8
+ // (iflow.js:5165 `BMi`: writeFileSync(JSON.stringify(settings, null, 2)); comments go, the mode stays).
9
+ // - A custom OpenAI-compatible endpoint is four top-level keys: selectedAuthType "openai-compatible" (the enum's
10
+ // OPENAI_COMPATIBLE, iflow.js:974), apiKey, baseUrl (requests go to `${baseUrl}/chat/completions`, iflow.js:956) and
11
+ // modelName (any name is accepted for openai-compatible, `validateModel`). The settings win over IFLOW_* variables only
12
+ // when those are unset: IFLOW_apiKey / IFLOW_API_KEY / iflow_apiKey ... and the same for baseUrl, modelName and
13
+ // selectedAuthType override the file (iflow.js:974 `CT`, `zOt`), and a project's .iflow/settings.json overrides the
14
+ // user file key by key.
15
+ // - The key: only `apiKey` in a settings file or an IFLOW_apiKey variable; there is no key-file or command option. The one
16
+ // .env file iFlow CLI loads is the first found walking up from the working folder (iflow.js:5165 `nnu`), so ~/.iflow/.env
17
+ // is skipped in any project that has its own .env: the key is copied into settings.json, which is then 0600.
18
+ // - Headers: fixed in the OpenAI client (Authorization: Bearer, user-agent, session ids, iflow.js:956); there is no
19
+ // custom-header setting, so `x-100x-context-edit` cannot be sent.
20
+ // - Display: iFlow CLI shows modelName as it is (no display-name setting for openai-compatible models).
21
+ // The person's own values of the four keys are recorded and put back by disconnect; nothing else in the file changes, and
22
+ // comments are kept (the shared JSON-with-comments codec in qwen-code.ts).
23
+ import { join } from "node:path"
24
+ import { type Connector, readKey, routeUrl } from "../common.ts"
25
+ import { jsonc } from "./qwen-code.ts"
26
+
27
+ export const connectors: Connector[] = [
28
+ {
29
+ id: "iflow-cli",
30
+ label: "iFlow CLI",
31
+ file: (home) => join(home, ".iflow", "settings.json"),
32
+ probe: [".iflow"],
33
+ authHeader: "authorization",
34
+ edits: (home, s) => [
35
+ {
36
+ path: join(home, ".iflow", "settings.json"),
37
+ format: "custom",
38
+ codec: jsonc(),
39
+ holdsKey: true,
40
+ secretKeys: ["apiKey"],
41
+ values: { selectedAuthType: "openai-compatible", baseUrl: `${routeUrl(s)}/v1`, modelName: s.model, apiKey: readKey(home) },
42
+ },
43
+ ],
44
+ notes: [
45
+ "iFlow CLI keeps the key in ~/.iflow/settings.json (apiKey; it has no key-file option), so that file is now readable by you only (0600). Its sign-in is set to OpenAI-compatible.",
46
+ "iFlow CLI cannot send extra headers: context editing applies only when the gateway turns it on for everyone. It has no display name for the model, so it shows the gateway's model id.",
47
+ "IFLOW_apiKey, IFLOW_baseUrl or IFLOW_modelName (or IFLOW_API_KEY ...) set in your shell, or a project's .iflow/settings.json, take precedence over these settings.",
48
+ ],
49
+ },
50
+ ]
@@ -0,0 +1,86 @@
1
+ // Every gateway connector besides the four in connect.ts: one file per tool, each exporting `connectors` (empty when the
2
+ // tool cannot be pointed at the gateway; its file says why).
3
+ import type { Connector } from "../common.ts"
4
+ import { connectors as c_continue } from "./continue.ts"
5
+ import { connectors as c_pearai } from "./pearai.ts"
6
+ import { connectors as c_aider } from "./aider.ts"
7
+ import { connectors as c_goose } from "./goose.ts"
8
+ import { connectors as c_crush } from "./crush.ts"
9
+ import { connectors as c_kimi_code } from "./kimi-code.ts"
10
+ import { connectors as c_qwen_code } from "./qwen-code.ts"
11
+ import { connectors as c_iflow_cli } from "./iflow-cli.ts"
12
+ import { connectors as c_mistral_vibe } from "./mistral-vibe.ts"
13
+ import { connectors as c_grok_cli } from "./grok-cli.ts"
14
+ import { connectors as c_openhands } from "./openhands.ts"
15
+ import { connectors as c_letta_code } from "./letta-code.ts"
16
+ import { connectors as c_factory_droid } from "./factory-droid.ts"
17
+ import { connectors as c_hermes } from "./hermes.ts"
18
+ import { connectors as c_pi } from "./pi.ts"
19
+ import { connectors as c_omp } from "./omp.ts"
20
+ import { connectors as c_mastracode } from "./mastracode.ts"
21
+ import { connectors as c_muse } from "./muse.ts"
22
+ import { connectors as c_roo } from "./roo.ts"
23
+ import { connectors as c_kilo_code } from "./kilo-code.ts"
24
+ import { connectors as c_kilo_cli } from "./kilo-cli.ts"
25
+ import { connectors as c_zed } from "./zed.ts"
26
+ import { connectors as c_vscode_copilot } from "./vscode-copilot.ts"
27
+ import { connectors as c_cline } from "./cline.ts"
28
+ import { connectors as c_cline_sdk } from "./cline-sdk.ts"
29
+ import { connectors as c_cursor } from "./cursor.ts"
30
+ import { connectors as c_cursor_cli } from "./cursor-cli.ts"
31
+ import { connectors as c_copilot_cli } from "./copilot-cli.ts"
32
+ import { connectors as c_windsurf } from "./windsurf.ts"
33
+ import { connectors as c_antigravity } from "./antigravity.ts"
34
+ import { connectors as c_amazon_q_cli } from "./amazon-q-cli.ts"
35
+ import { connectors as c_amazon_q_ide } from "./amazon-q-ide.ts"
36
+ import { connectors as c_kiro_cli } from "./kiro-cli.ts"
37
+ import { connectors as c_kiro_ide } from "./kiro-ide.ts"
38
+ import { connectors as c_auggie } from "./auggie.ts"
39
+ import { connectors as c_codebuff } from "./codebuff.ts"
40
+ import { connectors as c_tabnine_cli } from "./tabnine-cli.ts"
41
+ import { connectors as c_devin } from "./devin.ts"
42
+ import { connectors as c_qoder_cli } from "./qoder-cli.ts"
43
+ import { connectors as c_void } from "./void.ts"
44
+
45
+ export const EXTRA_CONNECTORS: Connector[] = [
46
+ ...c_continue,
47
+ ...c_pearai,
48
+ ...c_aider,
49
+ ...c_goose,
50
+ ...c_crush,
51
+ ...c_kimi_code,
52
+ ...c_qwen_code,
53
+ ...c_iflow_cli,
54
+ ...c_mistral_vibe,
55
+ ...c_grok_cli,
56
+ ...c_openhands,
57
+ ...c_letta_code,
58
+ ...c_factory_droid,
59
+ ...c_hermes,
60
+ ...c_pi,
61
+ ...c_omp,
62
+ ...c_mastracode,
63
+ ...c_muse,
64
+ ...c_roo,
65
+ ...c_kilo_code,
66
+ ...c_kilo_cli,
67
+ ...c_zed,
68
+ ...c_vscode_copilot,
69
+ ...c_cline,
70
+ ...c_cline_sdk,
71
+ ...c_cursor,
72
+ ...c_cursor_cli,
73
+ ...c_copilot_cli,
74
+ ...c_windsurf,
75
+ ...c_antigravity,
76
+ ...c_amazon_q_cli,
77
+ ...c_amazon_q_ide,
78
+ ...c_kiro_cli,
79
+ ...c_kiro_ide,
80
+ ...c_auggie,
81
+ ...c_codebuff,
82
+ ...c_tabnine_cli,
83
+ ...c_devin,
84
+ ...c_qoder_cli,
85
+ ...c_void,
86
+ ]