@nanonets/graft 0.15.0 → 0.18.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.
Files changed (259) hide show
  1. package/README.md +17 -5
  2. package/dist/ai/crux.d.ts +14 -1
  3. package/dist/ai/crux.d.ts.map +1 -1
  4. package/dist/ai/crux.js +31 -1
  5. package/dist/ai/crux.js.map +1 -1
  6. package/dist/ai/failure.d.ts +13 -2
  7. package/dist/ai/failure.d.ts.map +1 -1
  8. package/dist/ai/failure.js +16 -4
  9. package/dist/ai/failure.js.map +1 -1
  10. package/dist/ai/llm/factory.d.ts +6 -1
  11. package/dist/ai/llm/factory.d.ts.map +1 -1
  12. package/dist/ai/llm/factory.js +8 -0
  13. package/dist/ai/llm/factory.js.map +1 -1
  14. package/dist/ai/llm/orcarouter.d.ts +34 -0
  15. package/dist/ai/llm/orcarouter.d.ts.map +1 -0
  16. package/dist/ai/llm/orcarouter.js +45 -0
  17. package/dist/ai/llm/orcarouter.js.map +1 -0
  18. package/dist/ai/providers.d.ts.map +1 -1
  19. package/dist/ai/providers.js +13 -3
  20. package/dist/ai/providers.js.map +1 -1
  21. package/dist/app/brain-build.d.ts +66 -0
  22. package/dist/app/brain-build.d.ts.map +1 -0
  23. package/dist/app/brain-build.js +169 -0
  24. package/dist/app/brain-build.js.map +1 -0
  25. package/dist/app/checkout.d.ts +53 -3
  26. package/dist/app/checkout.d.ts.map +1 -1
  27. package/dist/app/checkout.js +195 -27
  28. package/dist/app/checkout.js.map +1 -1
  29. package/dist/app/events.d.ts +7 -0
  30. package/dist/app/events.d.ts.map +1 -1
  31. package/dist/app/events.js +7 -2
  32. package/dist/app/events.js.map +1 -1
  33. package/dist/app/history.d.ts +128 -0
  34. package/dist/app/history.d.ts.map +1 -0
  35. package/dist/app/history.js +269 -0
  36. package/dist/app/history.js.map +1 -0
  37. package/dist/app/identity.d.ts +33 -0
  38. package/dist/app/identity.d.ts.map +1 -1
  39. package/dist/app/identity.js +72 -0
  40. package/dist/app/identity.js.map +1 -1
  41. package/dist/app/main.js +12 -0
  42. package/dist/app/main.js.map +1 -1
  43. package/dist/app/pages.d.ts +17 -0
  44. package/dist/app/pages.d.ts.map +1 -1
  45. package/dist/app/pages.js +112 -5
  46. package/dist/app/pages.js.map +1 -1
  47. package/dist/app/review-process.d.ts +66 -0
  48. package/dist/app/review-process.d.ts.map +1 -0
  49. package/dist/app/review-process.js +199 -0
  50. package/dist/app/review-process.js.map +1 -0
  51. package/dist/app/review-worker.d.ts +2 -0
  52. package/dist/app/review-worker.d.ts.map +1 -0
  53. package/dist/app/review-worker.js +108 -0
  54. package/dist/app/review-worker.js.map +1 -0
  55. package/dist/app/review.d.ts.map +1 -1
  56. package/dist/app/review.js +14 -1
  57. package/dist/app/review.js.map +1 -1
  58. package/dist/app/server.d.ts +22 -2
  59. package/dist/app/server.d.ts.map +1 -1
  60. package/dist/app/server.js +77 -3
  61. package/dist/app/server.js.map +1 -1
  62. package/dist/app/sources.d.ts +68 -0
  63. package/dist/app/sources.d.ts.map +1 -0
  64. package/dist/app/sources.js +341 -0
  65. package/dist/app/sources.js.map +1 -0
  66. package/dist/ask/ask.d.ts +6 -0
  67. package/dist/ask/ask.d.ts.map +1 -1
  68. package/dist/ask/ask.js +30 -2
  69. package/dist/ask/ask.js.map +1 -1
  70. package/dist/blast/blast-cli.d.ts.map +1 -1
  71. package/dist/blast/blast-cli.js +6 -30
  72. package/dist/blast/blast-cli.js.map +1 -1
  73. package/dist/blast/name.d.ts +13 -0
  74. package/dist/blast/name.d.ts.map +1 -1
  75. package/dist/blast/name.js +36 -0
  76. package/dist/blast/name.js.map +1 -1
  77. package/dist/brain/attach.d.ts +44 -0
  78. package/dist/brain/attach.d.ts.map +1 -0
  79. package/dist/brain/attach.js +63 -0
  80. package/dist/brain/attach.js.map +1 -0
  81. package/dist/brain/connect.d.ts +59 -0
  82. package/dist/brain/connect.d.ts.map +1 -0
  83. package/dist/brain/connect.js +80 -0
  84. package/dist/brain/connect.js.map +1 -0
  85. package/dist/brain/link.d.ts +82 -0
  86. package/dist/brain/link.d.ts.map +1 -0
  87. package/dist/brain/link.js +136 -0
  88. package/dist/brain/link.js.map +1 -0
  89. package/dist/brain/push.d.ts +72 -0
  90. package/dist/brain/push.d.ts.map +1 -0
  91. package/dist/brain/push.js +191 -0
  92. package/dist/brain/push.js.map +1 -0
  93. package/dist/brain/wire.d.ts +38 -0
  94. package/dist/brain/wire.d.ts.map +1 -0
  95. package/dist/brain/wire.js +133 -0
  96. package/dist/brain/wire.js.map +1 -0
  97. package/dist/claude/format.d.ts.map +1 -1
  98. package/dist/claude/format.js +10 -3
  99. package/dist/claude/format.js.map +1 -1
  100. package/dist/claude/hooks.d.ts.map +1 -1
  101. package/dist/claude/hooks.js +125 -24
  102. package/dist/claude/hooks.js.map +1 -1
  103. package/dist/claude/init.d.ts +6 -0
  104. package/dist/claude/init.d.ts.map +1 -1
  105. package/dist/claude/init.js +10 -2
  106. package/dist/claude/init.js.map +1 -1
  107. package/dist/claude/session-metrics.d.ts +73 -0
  108. package/dist/claude/session-metrics.d.ts.map +1 -0
  109. package/dist/claude/session-metrics.js +206 -0
  110. package/dist/claude/session-metrics.js.map +1 -0
  111. package/dist/claude/settings-merge.d.ts +25 -1
  112. package/dist/claude/settings-merge.d.ts.map +1 -1
  113. package/dist/claude/settings-merge.js +88 -25
  114. package/dist/claude/settings-merge.js.map +1 -1
  115. package/dist/claude/skill-template.d.ts.map +1 -1
  116. package/dist/claude/skill-template.js +6 -0
  117. package/dist/claude/skill-template.js.map +1 -1
  118. package/dist/claude/state.d.ts +26 -0
  119. package/dist/claude/state.d.ts.map +1 -1
  120. package/dist/claude/state.js +14 -1
  121. package/dist/claude/state.js.map +1 -1
  122. package/dist/claude/tally.d.ts +27 -0
  123. package/dist/claude/tally.d.ts.map +1 -1
  124. package/dist/claude/tally.js +74 -1
  125. package/dist/claude/tally.js.map +1 -1
  126. package/dist/cli.js +237 -4
  127. package/dist/cli.js.map +1 -1
  128. package/dist/context/build.d.ts +11 -0
  129. package/dist/context/build.d.ts.map +1 -1
  130. package/dist/context/build.js +27 -6
  131. package/dist/context/build.js.map +1 -1
  132. package/dist/context/check.d.ts.map +1 -1
  133. package/dist/context/check.js +5 -13
  134. package/dist/context/check.js.map +1 -1
  135. package/dist/context/node-file.d.ts +1 -1
  136. package/dist/context/node-file.d.ts.map +1 -1
  137. package/dist/context/node-file.js +38 -6
  138. package/dist/context/node-file.js.map +1 -1
  139. package/dist/context/price.d.ts +49 -0
  140. package/dist/context/price.d.ts.map +1 -0
  141. package/dist/context/price.js +83 -0
  142. package/dist/context/price.js.map +1 -0
  143. package/dist/context/savings.d.ts +20 -2
  144. package/dist/context/savings.d.ts.map +1 -1
  145. package/dist/context/savings.js +57 -4
  146. package/dist/context/savings.js.map +1 -1
  147. package/dist/engine.d.ts +2 -0
  148. package/dist/engine.d.ts.map +1 -1
  149. package/dist/engine.js +1 -0
  150. package/dist/engine.js.map +1 -1
  151. package/dist/graph/cards.d.ts +4 -3
  152. package/dist/graph/cards.d.ts.map +1 -1
  153. package/dist/graph/cards.js +34 -6
  154. package/dist/graph/cards.js.map +1 -1
  155. package/dist/graph/enrich.d.ts +4 -4
  156. package/dist/graph/enrich.d.ts.map +1 -1
  157. package/dist/graph/enrich.js +32 -6
  158. package/dist/graph/enrich.js.map +1 -1
  159. package/dist/graph/generic.d.ts +10 -0
  160. package/dist/graph/generic.d.ts.map +1 -1
  161. package/dist/graph/generic.js +20 -2
  162. package/dist/graph/generic.js.map +1 -1
  163. package/dist/graph/workspace.d.ts +2 -0
  164. package/dist/graph/workspace.d.ts.map +1 -1
  165. package/dist/graph/workspace.js +173 -6
  166. package/dist/graph/workspace.js.map +1 -1
  167. package/dist/hosts/antigravity.d.ts +2 -2
  168. package/dist/hosts/antigravity.d.ts.map +1 -1
  169. package/dist/hosts/claude-global.d.ts +33 -0
  170. package/dist/hosts/claude-global.d.ts.map +1 -0
  171. package/dist/hosts/claude-global.js +118 -0
  172. package/dist/hosts/claude-global.js.map +1 -0
  173. package/dist/hosts/codex-hooks.d.ts +2 -6
  174. package/dist/hosts/codex-hooks.d.ts.map +1 -1
  175. package/dist/hosts/codex-hooks.js +7 -30
  176. package/dist/hosts/codex-hooks.js.map +1 -1
  177. package/dist/hosts/config-write.d.ts +36 -0
  178. package/dist/hosts/config-write.d.ts.map +1 -0
  179. package/dist/hosts/config-write.js +63 -0
  180. package/dist/hosts/config-write.js.map +1 -0
  181. package/dist/hosts/cursor-hooks.d.ts +18 -0
  182. package/dist/hosts/cursor-hooks.d.ts.map +1 -0
  183. package/dist/hosts/cursor-hooks.js +100 -0
  184. package/dist/hosts/cursor-hooks.js.map +1 -0
  185. package/dist/hosts/init.d.ts +2 -2
  186. package/dist/hosts/init.d.ts.map +1 -1
  187. package/dist/hosts/init.js +8 -2
  188. package/dist/hosts/init.js.map +1 -1
  189. package/dist/hosts/mcp-config.d.ts +3 -5
  190. package/dist/hosts/mcp-config.d.ts.map +1 -1
  191. package/dist/hosts/mcp-config.js +5 -10
  192. package/dist/hosts/mcp-config.js.map +1 -1
  193. package/dist/hosts/plan.d.ts.map +1 -1
  194. package/dist/hosts/plan.js +7 -1
  195. package/dist/hosts/plan.js.map +1 -1
  196. package/dist/hosts/retract.d.ts.map +1 -1
  197. package/dist/hosts/retract.js +33 -13
  198. package/dist/hosts/retract.js.map +1 -1
  199. package/dist/hosts/sections.d.ts +23 -2
  200. package/dist/hosts/sections.d.ts.map +1 -1
  201. package/dist/hosts/sections.js +25 -9
  202. package/dist/hosts/sections.js.map +1 -1
  203. package/dist/index.d.ts +2 -0
  204. package/dist/index.d.ts.map +1 -1
  205. package/dist/index.js +2 -0
  206. package/dist/index.js.map +1 -1
  207. package/dist/ingest/fs.d.ts +20 -0
  208. package/dist/ingest/fs.d.ts.map +1 -1
  209. package/dist/ingest/fs.js +76 -3
  210. package/dist/ingest/fs.js.map +1 -1
  211. package/dist/mcp/server.d.ts.map +1 -1
  212. package/dist/mcp/server.js +33 -4
  213. package/dist/mcp/server.js.map +1 -1
  214. package/dist/mcp/tool-names.d.ts +20 -0
  215. package/dist/mcp/tool-names.d.ts.map +1 -0
  216. package/dist/mcp/tool-names.js +36 -0
  217. package/dist/mcp/tool-names.js.map +1 -0
  218. package/dist/mcp/tools.d.ts +11 -2
  219. package/dist/mcp/tools.d.ts.map +1 -1
  220. package/dist/mcp/tools.js +14 -22
  221. package/dist/mcp/tools.js.map +1 -1
  222. package/dist/telemetry/contract.d.ts.map +1 -1
  223. package/dist/telemetry/contract.js +10 -1
  224. package/dist/telemetry/contract.js.map +1 -1
  225. package/dist/telemetry/flush.d.ts.map +1 -1
  226. package/dist/telemetry/flush.js +1 -0
  227. package/dist/telemetry/flush.js.map +1 -1
  228. package/dist/telemetry/gate.d.ts +14 -0
  229. package/dist/telemetry/gate.d.ts.map +1 -1
  230. package/dist/telemetry/gate.js +18 -2
  231. package/dist/telemetry/gate.js.map +1 -1
  232. package/dist/telemetry/identity.d.ts +7 -0
  233. package/dist/telemetry/identity.d.ts.map +1 -1
  234. package/dist/telemetry/identity.js.map +1 -1
  235. package/dist/telemetry/index.d.ts +2 -2
  236. package/dist/telemetry/index.d.ts.map +1 -1
  237. package/dist/telemetry/index.js +2 -2
  238. package/dist/telemetry/index.js.map +1 -1
  239. package/dist/telemetry/sessions.d.ts +19 -2
  240. package/dist/telemetry/sessions.d.ts.map +1 -1
  241. package/dist/telemetry/sessions.js +57 -40
  242. package/dist/telemetry/sessions.js.map +1 -1
  243. package/dist/telemetry/track.d.ts +22 -3
  244. package/dist/telemetry/track.d.ts.map +1 -1
  245. package/dist/telemetry/track.js +34 -5
  246. package/dist/telemetry/track.js.map +1 -1
  247. package/dist/upkeep-run.d.ts.map +1 -1
  248. package/dist/upkeep-run.js +12 -2
  249. package/dist/upkeep-run.js.map +1 -1
  250. package/dist/upkeep.d.ts +25 -4
  251. package/dist/upkeep.d.ts.map +1 -1
  252. package/dist/upkeep.js +40 -1
  253. package/dist/upkeep.js.map +1 -1
  254. package/dist/util/state.d.ts +9 -0
  255. package/dist/util/state.d.ts.map +1 -1
  256. package/dist/util/state.js.map +1 -1
  257. package/package.json +2 -2
  258. package/scripts/postinstall.mjs +42 -3
  259. package/scripts/run-tests.mjs +14 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/brain/attach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,EAAE,SAAS,EAAE,EAClB,KAAK,EAAE,OAAO,GAAG,IAAI,GACpB,WAAW,EAAE,CA+Bf;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE,CAS5D"}
@@ -0,0 +1,63 @@
1
+ /** Most rules to attach to one answer. */
2
+ export const MAX_ATTACHED_RULES = 6;
3
+ /**
4
+ * The rules governing the symbols in `pointers`, resolved against `graph`.
5
+ *
6
+ * Scoped to the answer, not the repo: attaching every rule a brain holds would
7
+ * turn a token-saving pack into a token-spending one, and `ask` prints the
8
+ * saving it claims. Capped for the same reason.
9
+ */
10
+ export function rulesForPointers(pointers, rules, graph) {
11
+ if (!rules.length || !graph)
12
+ return [];
13
+ // node id → its current pointer and body hash. One pass over the graph; the
14
+ // caller does this once per ask, not once per hit.
15
+ const byId = new Map();
16
+ for (const n of graph.nodes) {
17
+ byId.set(n.id, { pointer: `${n.path}:${n.span}`, hash: n.body_hash });
18
+ }
19
+ const wanted = new Set(pointers);
20
+ const out = [];
21
+ const seen = new Set();
22
+ for (const r of rules) {
23
+ const node = byId.get(r.symbol);
24
+ if (!node || !wanted.has(node.pointer))
25
+ continue;
26
+ // The same rule can be mined against two symbols in one answer; show it once.
27
+ if (seen.has(r.rule))
28
+ continue;
29
+ seen.add(r.rule);
30
+ out.push({
31
+ rule: r.rule,
32
+ pointer: node.pointer,
33
+ // No recorded fingerprint means the rule was mined before hashing existed
34
+ // (or against a symbol graft could not hash). Unknown is not stale: a
35
+ // false "the code moved" is worse than saying nothing about freshness.
36
+ stale: r.fingerprint !== '' && r.fingerprint !== node.hash,
37
+ sourceUrl: r.sourceUrl,
38
+ });
39
+ if (out.length === MAX_ATTACHED_RULES)
40
+ break;
41
+ }
42
+ return out;
43
+ }
44
+ /**
45
+ * Render the rules block appended to an `ask` pack.
46
+ *
47
+ * Deliberately terse. This rides along on every answer, so it has to earn its
48
+ * tokens: one line per rule, the pointer it governs, and a link only when there
49
+ * is one. The `[graft]` prefix is avoided — `sumSavingsFooters` scans for
50
+ * `[graft] tokens saved ≈` and nothing here should look like a savings line.
51
+ */
52
+ export function formatRules(applied) {
53
+ if (!applied.length)
54
+ return [];
55
+ const lines = ['', 'rules that govern these symbols'];
56
+ for (const a of applied) {
57
+ const flag = a.stale ? ' (STALE — the code changed since this was decided)' : '';
58
+ lines.push(`- ${a.rule}${flag}`);
59
+ lines.push(` ${a.pointer}${a.sourceUrl ? ` · ${a.sourceUrl}` : ''}`);
60
+ }
61
+ return lines;
62
+ }
63
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/brain/attach.ts"],"names":[],"mappings":"AA0BA,0CAA0C;AAC1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,KAAkB,EAClB,KAAqB;IAErB,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEvC,4EAA4E;IAC5E,mDAAmD;IACnD,MAAM,IAAI,GAAG,IAAI,GAAG,EAA6C,CAAC;IAClE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,SAAS;QACjD,8EAA8E;QAC9E,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QAC/B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,0EAA0E;YAC1E,sEAAsE;YACtE,uEAAuE;YACvE,KAAK,EAAE,CAAC,CAAC,WAAW,KAAK,EAAE,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,IAAI;YAC1D,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,kBAAkB;YAAE,MAAM;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAsB;IAChD,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,CAAC,EAAE,EAAE,iCAAiC,CAAC,CAAC;IACtD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The one command a user runs to attach a brain: parse the handoff value,
3
+ * store the link, pull the rules, write them into the agent files.
4
+ */
5
+ import { type BrainLink, type BrainRule } from './link.js';
6
+ import { type BrainWrite } from './wire.js';
7
+ /**
8
+ * Parse the `--brain` value.
9
+ *
10
+ * The onboarding page hands over one string so there is one thing to copy. Two
11
+ * accepted shapes:
12
+ *
13
+ * `<brainId>:<token>` — the normal case, both halves in one paste
14
+ * `<brainId>` — with the token in `GRAFT_BRAIN_TOKEN`, for CI, where
15
+ * a secret must not appear in a command line
16
+ *
17
+ * A bare token with no brain id is rejected rather than guessed: pointing the
18
+ * wrong brain at a repo would write another team's rules into it.
19
+ */
20
+ export declare function parseBrainArg(value: string): BrainLink | {
21
+ error: string;
22
+ };
23
+ /** What connecting a brain did, for the caller to print. */
24
+ export interface ConnectResult {
25
+ linked: boolean;
26
+ ruleCount: number;
27
+ writes: BrainWrite[];
28
+ /** Set when the rules could not be fetched; the link is still stored. */
29
+ warning?: string;
30
+ }
31
+ /**
32
+ * Store the link, pull the rules once, and write them into the agent files.
33
+ *
34
+ * The link is stored even when the pull fails. That is deliberate: a token
35
+ * typed correctly against a brain that is momentarily unreachable should not
36
+ * have to be pasted again — `graft brain pull` retries, and `ask` degrades to
37
+ * the code-only answer meanwhile.
38
+ */
39
+ export declare function connectBrain(repo: string, link: BrainLink, opts?: {
40
+ home: string;
41
+ ids?: string[];
42
+ fetchImpl?: typeof fetch;
43
+ }): Promise<ConnectResult>;
44
+ /**
45
+ * Re-pull the rules for an already-linked repo and rewrite the agent files.
46
+ * Returns null when the repo has no brain linked.
47
+ */
48
+ export declare function pullBrain(repo: string, opts: {
49
+ home: string;
50
+ ids?: string[];
51
+ fetchImpl?: typeof fetch;
52
+ }): Promise<ConnectResult | null>;
53
+ /** The linked brain and its cached rule set, for `graft brain status`. */
54
+ export declare function brainStatus(repo: string): {
55
+ link: BrainLink | null;
56
+ rules: BrainRule[];
57
+ fetchedAt: number | null;
58
+ };
59
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/brain/connect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAML,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAGhE;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAkB1E;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,EACf,IAAI,GAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAiB,GAC9E,OAAO,CAAC,aAAa,CAAC,CAexB;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,GAC/D,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAI/B;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;IACzC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAIA"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The one command a user runs to attach a brain: parse the handoff value,
3
+ * store the link, pull the rules, write them into the agent files.
4
+ */
5
+ import { readLink, writeLink, fetchRules, writeRulesCache, readRulesCache, } from './link.js';
6
+ import { writeBrainSections } from './wire.js';
7
+ import { basename } from 'node:path';
8
+ /**
9
+ * Parse the `--brain` value.
10
+ *
11
+ * The onboarding page hands over one string so there is one thing to copy. Two
12
+ * accepted shapes:
13
+ *
14
+ * `<brainId>:<token>` — the normal case, both halves in one paste
15
+ * `<brainId>` — with the token in `GRAFT_BRAIN_TOKEN`, for CI, where
16
+ * a secret must not appear in a command line
17
+ *
18
+ * A bare token with no brain id is rejected rather than guessed: pointing the
19
+ * wrong brain at a repo would write another team's rules into it.
20
+ */
21
+ export function parseBrainArg(value) {
22
+ const raw = value.trim();
23
+ if (!raw)
24
+ return { error: 'empty --brain value' };
25
+ const cut = raw.indexOf(':');
26
+ if (cut > 0) {
27
+ const brainId = raw.slice(0, cut).trim();
28
+ const token = raw.slice(cut + 1).trim();
29
+ if (!brainId || !token)
30
+ return { error: 'expected --brain <brainId>:<token>' };
31
+ return { brainId, token };
32
+ }
33
+ const envToken = process.env.GRAFT_BRAIN_TOKEN;
34
+ if (!envToken) {
35
+ return {
36
+ error: 'expected --brain <brainId>:<token>, or a bare brain id with the token in GRAFT_BRAIN_TOKEN',
37
+ };
38
+ }
39
+ return { brainId: raw, token: envToken };
40
+ }
41
+ /**
42
+ * Store the link, pull the rules once, and write them into the agent files.
43
+ *
44
+ * The link is stored even when the pull fails. That is deliberate: a token
45
+ * typed correctly against a brain that is momentarily unreachable should not
46
+ * have to be pasted again — `graft brain pull` retries, and `ask` degrades to
47
+ * the code-only answer meanwhile.
48
+ */
49
+ export async function connectBrain(repo, link, opts = { home: '' }) {
50
+ writeLink(repo, link);
51
+ const rules = await fetchRules(link, opts.fetchImpl ?? fetch);
52
+ if (rules === null) {
53
+ return {
54
+ linked: true,
55
+ ruleCount: 0,
56
+ writes: [],
57
+ warning: 'could not reach the brain to pull its rules — the link is saved; run `graft brain pull` to retry',
58
+ };
59
+ }
60
+ writeRulesCache(repo, { brainId: link.brainId, fetchedAt: Date.now(), rules });
61
+ const writes = writeBrainSections(repo, opts.home, rules, basename(repo), opts.ids);
62
+ return { linked: true, ruleCount: rules.length, writes };
63
+ }
64
+ /**
65
+ * Re-pull the rules for an already-linked repo and rewrite the agent files.
66
+ * Returns null when the repo has no brain linked.
67
+ */
68
+ export async function pullBrain(repo, opts) {
69
+ const link = readLink(repo);
70
+ if (!link)
71
+ return null;
72
+ return connectBrain(repo, link, opts);
73
+ }
74
+ /** The linked brain and its cached rule set, for `graft brain status`. */
75
+ export function brainStatus(repo) {
76
+ const link = readLink(repo);
77
+ const cache = readRulesCache(repo);
78
+ return { link, rules: cache?.rules ?? [], fetchedAt: cache?.fetchedAt ?? null };
79
+ }
80
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/brain/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /** How long a cached rule set is served before upkeep refreshes it. */
2
+ export declare const RULES_TTL_MS: number;
3
+ /**
4
+ * The TTL used while the cache holds NO rules.
5
+ *
6
+ * A brain is connected during onboarding while it is still being mined, so the
7
+ * first pull legitimately returns nothing. Six hours of that is the difference
8
+ * between the feature working and the user concluding it does not: they wired
9
+ * graft up, got an empty rulebook, and nothing would go back for the real one
10
+ * until tomorrow. Two minutes costs one cheap request per session until the
11
+ * rules land, and then the normal TTL takes over.
12
+ */
13
+ export declare const EMPTY_RULES_TTL_MS: number;
14
+ /** One rule from the brain, anchored to a symbol in this repo. */
15
+ export interface BrainRule {
16
+ ruleId: string;
17
+ /** A graft node id: `<path>#<QualifiedName>`. */
18
+ symbol: string;
19
+ /** The symbol's body hash when the rule was mined; '' when unrecorded. */
20
+ fingerprint: string;
21
+ rule: string;
22
+ /** Deep link to the commit or pull request that established it. */
23
+ sourceUrl?: string;
24
+ }
25
+ /** The cached rule set, as stored under the repo's graft cache. */
26
+ export interface RulesCache {
27
+ brainId: string;
28
+ fetchedAt: number;
29
+ rules: BrainRule[];
30
+ /** When a refresh was last ATTEMPTED, successful or not. Separate from
31
+ * `fetchedAt`, which records when rules last actually arrived: without the
32
+ * distinction, a brain that is still building would be re-checked on every
33
+ * single command, because its `fetchedAt` never advances. */
34
+ checkedAt?: number;
35
+ }
36
+ /** The persisted brain link. */
37
+ export interface BrainLink {
38
+ brainId: string;
39
+ /** Workspace API key (`nn...`). Stored in the git-ignored `.graft/`. */
40
+ token: string;
41
+ /** Set only when the user pointed graft at a non-default host. */
42
+ baseUrl?: string;
43
+ }
44
+ /**
45
+ * The link for repo `dir`, or null when it has none.
46
+ *
47
+ * `GRAFT_BRAIN_TOKEN` / `GRAFT_BRAIN_ID` override the stored pair, so CI can
48
+ * attach a brain without writing to the checkout.
49
+ */
50
+ export declare function readLink(dir: string): BrainLink | null;
51
+ /** Persist the link for repo `dir`, merging into any existing build config. */
52
+ export declare function writeLink(dir: string, link: BrainLink): void;
53
+ /** Forget the link for repo `dir`. Leaves the cached rules for `uninstall` to remove. */
54
+ export declare function clearLink(dir: string): void;
55
+ /** Where the rule cache lives: beside the graph, since it is derived data. */
56
+ export declare function rulesCachePath(dir: string): string;
57
+ export declare function readRulesCache(dir: string): RulesCache | null;
58
+ export declare function writeRulesCache(dir: string, cache: RulesCache): void;
59
+ /**
60
+ * Whether a cached set is old enough to refetch.
61
+ *
62
+ * Keyed off the last ATTEMPT, not the last success, and with a much shorter
63
+ * window while the cache is empty — see EMPTY_RULES_TTL_MS for why that case
64
+ * is the one that matters.
65
+ */
66
+ export declare function cacheIsStale(cache: RulesCache | null, now?: number): boolean;
67
+ /** Record that a refresh was attempted, without claiming rules arrived. */
68
+ export declare function markRulesChecked(dir: string, now?: number): void;
69
+ /**
70
+ * The base URL for a link, honouring the env override first so a misconfigured
71
+ * stored value can always be corrected without editing a file.
72
+ */
73
+ export declare function baseUrlFor(link: BrainLink): string;
74
+ /**
75
+ * Fetch the brain's symbol-anchored rules.
76
+ *
77
+ * Returns null on any failure — an unreachable brain, a revoked token, a
78
+ * malformed body. Never throws: `ask` degrades to the code-only answer it gave
79
+ * before a brain was attached, which is the whole reason the cache exists.
80
+ */
81
+ export declare function fetchRules(link: BrainLink, fetchImpl?: typeof fetch): Promise<BrainRule[] | null>;
82
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/brain/link.ts"],"names":[],"mappings":"AAoBA,uEAAuE;AACvE,eAAO,MAAM,YAAY,QAAqB,CAAC;AAE/C;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,QAAgB,CAAC;AAEhD,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;iEAG6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,gCAAgC;AAChC,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAStD;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAE5D;AAED,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAE7D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,SAAa,GAAG,OAAO,CAIhF;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI,CAIpE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAElD;AAeD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,SAAS,EACf,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CA4B7B"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * The link between a repo's graft graph and a Trail brain.
3
+ *
4
+ * A brain holds the rules mined out of this repository's history — what the
5
+ * team decided and why, which the code graph cannot see. Graft holds the code
6
+ * and can say which symbols an answer touches. This module is the join: the
7
+ * token identifying the brain, and the rules cached beside it so `ask` never
8
+ * waits on the network.
9
+ *
10
+ * The link lives in the per-repo, git-ignored `.graft/config.json`, next to the
11
+ * other persisted build choices. Not in `~/.graft/`: a brain belongs to one
12
+ * repository, and two checkouts of different repos on one machine must not
13
+ * share one.
14
+ */
15
+ import { readBuildConfig, patchBuildConfig, cacheDir, readJson, writeJsonAtomic } from '../util/state.js';
16
+ import { join } from 'node:path';
17
+ /** Default API host. Overridden by GRAFT_BRAIN_URL, for staging and self-hosted. */
18
+ const DEFAULT_BRAIN_BASE_URL = 'https://agents.nanonets.com';
19
+ /** How long a cached rule set is served before upkeep refreshes it. */
20
+ export const RULES_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours
21
+ /**
22
+ * The TTL used while the cache holds NO rules.
23
+ *
24
+ * A brain is connected during onboarding while it is still being mined, so the
25
+ * first pull legitimately returns nothing. Six hours of that is the difference
26
+ * between the feature working and the user concluding it does not: they wired
27
+ * graft up, got an empty rulebook, and nothing would go back for the real one
28
+ * until tomorrow. Two minutes costs one cheap request per session until the
29
+ * rules land, and then the normal TTL takes over.
30
+ */
31
+ export const EMPTY_RULES_TTL_MS = 2 * 60 * 1000;
32
+ /**
33
+ * The link for repo `dir`, or null when it has none.
34
+ *
35
+ * `GRAFT_BRAIN_TOKEN` / `GRAFT_BRAIN_ID` override the stored pair, so CI can
36
+ * attach a brain without writing to the checkout.
37
+ */
38
+ export function readLink(dir) {
39
+ const envToken = process.env.GRAFT_BRAIN_TOKEN;
40
+ const envBrain = process.env.GRAFT_BRAIN_ID;
41
+ if (envToken && envBrain) {
42
+ return { brainId: envBrain, token: envToken, baseUrl: process.env.GRAFT_BRAIN_URL };
43
+ }
44
+ const stored = readBuildConfig(dir)?.brain;
45
+ if (!stored?.brainId || !stored?.token)
46
+ return null;
47
+ return stored;
48
+ }
49
+ /** Persist the link for repo `dir`, merging into any existing build config. */
50
+ export function writeLink(dir, link) {
51
+ patchBuildConfig(dir, { brain: link });
52
+ }
53
+ /** Forget the link for repo `dir`. Leaves the cached rules for `uninstall` to remove. */
54
+ export function clearLink(dir) {
55
+ patchBuildConfig(dir, { brain: undefined });
56
+ }
57
+ /** Where the rule cache lives: beside the graph, since it is derived data. */
58
+ export function rulesCachePath(dir) {
59
+ return join(cacheDir(dir), 'brain-rules.json');
60
+ }
61
+ export function readRulesCache(dir) {
62
+ return readJson(rulesCachePath(dir));
63
+ }
64
+ export function writeRulesCache(dir, cache) {
65
+ writeJsonAtomic(rulesCachePath(dir), cache, true);
66
+ }
67
+ /**
68
+ * Whether a cached set is old enough to refetch.
69
+ *
70
+ * Keyed off the last ATTEMPT, not the last success, and with a much shorter
71
+ * window while the cache is empty — see EMPTY_RULES_TTL_MS for why that case
72
+ * is the one that matters.
73
+ */
74
+ export function cacheIsStale(cache, now = Date.now()) {
75
+ if (!cache)
76
+ return true;
77
+ const ttl = cache.rules.length === 0 ? EMPTY_RULES_TTL_MS : RULES_TTL_MS;
78
+ return now - (cache.checkedAt ?? cache.fetchedAt) > ttl;
79
+ }
80
+ /** Record that a refresh was attempted, without claiming rules arrived. */
81
+ export function markRulesChecked(dir, now = Date.now()) {
82
+ const cache = readRulesCache(dir);
83
+ if (!cache)
84
+ return;
85
+ writeRulesCache(dir, { ...cache, checkedAt: now });
86
+ }
87
+ /**
88
+ * The base URL for a link, honouring the env override first so a misconfigured
89
+ * stored value can always be corrected without editing a file.
90
+ */
91
+ export function baseUrlFor(link) {
92
+ return (process.env.GRAFT_BRAIN_URL || link.baseUrl || DEFAULT_BRAIN_BASE_URL).replace(/\/+$/, '');
93
+ }
94
+ /** Timeout for one rules fetch. Short: `ask` must never block on this. */
95
+ const FETCH_TIMEOUT_MS = 5000;
96
+ /**
97
+ * Fetch the brain's symbol-anchored rules.
98
+ *
99
+ * Returns null on any failure — an unreachable brain, a revoked token, a
100
+ * malformed body. Never throws: `ask` degrades to the code-only answer it gave
101
+ * before a brain was attached, which is the whole reason the cache exists.
102
+ */
103
+ export async function fetchRules(link, fetchImpl = fetch) {
104
+ // The PUBLIC, token-authenticated route: graft runs on a laptop with no
105
+ // session, and the token it holds is scoped to this one brain rather than the
106
+ // workspace. The protected route is for the app itself.
107
+ const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/rules/anchors`;
108
+ try {
109
+ const res = await fetchImpl(url, {
110
+ headers: { authorization: `Bearer ${link.token}`, accept: 'application/json' },
111
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
112
+ });
113
+ if (!res.ok)
114
+ return null;
115
+ const body = (await res.json());
116
+ if (!Array.isArray(body.anchors))
117
+ return null;
118
+ const out = [];
119
+ for (const a of body.anchors) {
120
+ if (!a?.symbol || !a?.rule)
121
+ continue;
122
+ out.push({
123
+ ruleId: String(a.rule_id ?? ''),
124
+ symbol: a.symbol,
125
+ fingerprint: String(a.fingerprint ?? ''),
126
+ rule: a.rule,
127
+ sourceUrl: a.source_url || undefined,
128
+ });
129
+ }
130
+ return out;
131
+ }
132
+ catch {
133
+ return null;
134
+ }
135
+ }
136
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/brain/link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { type RepoDigest } from "../app/history.js";
2
+ import type { GraphV1 } from "../graph/types.js";
3
+ import { type BrainLink } from "./link.js";
4
+ /** What a local ingest read, for the caller to print. */
5
+ export interface PushResult {
6
+ jobId: string;
7
+ repo: string;
8
+ commits: number;
9
+ threads: number;
10
+ symbols: number;
11
+ sources: number;
12
+ /** Set when discussion could not be read; the ingest still went ahead. */
13
+ warning?: string;
14
+ }
15
+ /**
16
+ * owner/name for this checkout, from its origin remote.
17
+ *
18
+ * Only GitHub, because the discussion reader and the forge links are
19
+ * GitHub-shaped; another forge would need its own reader, not a looser regex
20
+ * here that produces links to nowhere.
21
+ */
22
+ export declare function repoSlugFromGit(root: string): {
23
+ owner: string;
24
+ name: string;
25
+ } | null;
26
+ /**
27
+ * A GitHub token from wherever the user already keeps one.
28
+ *
29
+ * `gh auth token` first, because anyone who works with private repositories on
30
+ * the command line has it and it needs no setup at all. The env vars are the
31
+ * CI path. Absent, the ingest still runs on commits alone — pull-request
32
+ * discussion is the richest source, not a required one.
33
+ */
34
+ export declare function githubToken(): string | null;
35
+ /** What the brain is waiting for, when it is waiting for something. */
36
+ export interface ExpectedRepo {
37
+ slug: string;
38
+ status: string;
39
+ brainName: string;
40
+ }
41
+ /**
42
+ * The repository this brain expects, or null when it expects none.
43
+ *
44
+ * The website records it when the user chooses the local route, so this is how
45
+ * the CLI knows whether the directory it is standing in is the one they asked
46
+ * for. Mining the wrong repo into a brain is silent and very hard to notice
47
+ * afterwards — the rules simply describe someone else's codebase — so the round
48
+ * trip is worth it.
49
+ *
50
+ * Null on any failure, which keeps an older brain (or an unreachable one)
51
+ * working exactly as it did before this check existed.
52
+ */
53
+ export declare function fetchExpectedRepo(link: BrainLink, fetchImpl?: typeof fetch): Promise<ExpectedRepo | null>;
54
+ /** Whether two repo slugs name the same repository. GitHub is case-insensitive. */
55
+ export declare function sameRepo(a: string, b: string): boolean;
56
+ /** Build the digest for the checkout at `root`. Reads nothing but text. */
57
+ export declare function buildLocalDigest(root: string, graph: GraphV1 | null, opts?: {
58
+ autoApprove?: boolean;
59
+ fetchImpl?: typeof fetch;
60
+ }): Promise<{
61
+ digest: RepoDigest;
62
+ warning?: string;
63
+ } | {
64
+ error: string;
65
+ }>;
66
+ /** Send a digest to the brain and return the job to poll. */
67
+ export declare function pushDigest(link: BrainLink, digest: RepoDigest, fetchImpl?: typeof fetch): Promise<{
68
+ jobId: string;
69
+ } | {
70
+ error: string;
71
+ }>;
72
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/brain/push.ts"],"names":[],"mappings":"AAiBA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAEvD,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAYD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMpF;AAQD;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAM3C;AAgBD,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAatH;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,2EAA2E;AAC3E,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GAAG,IAAI,EACrB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAC7D,OAAO,CAAC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDvE;AAED,6DAA6D;AAC7D,wBAAsB,UAAU,CAC9B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAkBhD"}