@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.
- package/README.md +17 -5
- package/dist/ai/crux.d.ts +14 -1
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +31 -1
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/failure.d.ts +13 -2
- package/dist/ai/failure.d.ts.map +1 -1
- package/dist/ai/failure.js +16 -4
- package/dist/ai/failure.js.map +1 -1
- package/dist/ai/llm/factory.d.ts +6 -1
- package/dist/ai/llm/factory.d.ts.map +1 -1
- package/dist/ai/llm/factory.js +8 -0
- package/dist/ai/llm/factory.js.map +1 -1
- package/dist/ai/llm/orcarouter.d.ts +34 -0
- package/dist/ai/llm/orcarouter.d.ts.map +1 -0
- package/dist/ai/llm/orcarouter.js +45 -0
- package/dist/ai/llm/orcarouter.js.map +1 -0
- package/dist/ai/providers.d.ts.map +1 -1
- package/dist/ai/providers.js +13 -3
- package/dist/ai/providers.js.map +1 -1
- package/dist/app/brain-build.d.ts +66 -0
- package/dist/app/brain-build.d.ts.map +1 -0
- package/dist/app/brain-build.js +169 -0
- package/dist/app/brain-build.js.map +1 -0
- package/dist/app/checkout.d.ts +53 -3
- package/dist/app/checkout.d.ts.map +1 -1
- package/dist/app/checkout.js +195 -27
- package/dist/app/checkout.js.map +1 -1
- package/dist/app/events.d.ts +7 -0
- package/dist/app/events.d.ts.map +1 -1
- package/dist/app/events.js +7 -2
- package/dist/app/events.js.map +1 -1
- package/dist/app/history.d.ts +128 -0
- package/dist/app/history.d.ts.map +1 -0
- package/dist/app/history.js +269 -0
- package/dist/app/history.js.map +1 -0
- package/dist/app/identity.d.ts +33 -0
- package/dist/app/identity.d.ts.map +1 -1
- package/dist/app/identity.js +72 -0
- package/dist/app/identity.js.map +1 -1
- package/dist/app/main.js +12 -0
- package/dist/app/main.js.map +1 -1
- package/dist/app/pages.d.ts +17 -0
- package/dist/app/pages.d.ts.map +1 -1
- package/dist/app/pages.js +112 -5
- package/dist/app/pages.js.map +1 -1
- package/dist/app/review-process.d.ts +66 -0
- package/dist/app/review-process.d.ts.map +1 -0
- package/dist/app/review-process.js +199 -0
- package/dist/app/review-process.js.map +1 -0
- package/dist/app/review-worker.d.ts +2 -0
- package/dist/app/review-worker.d.ts.map +1 -0
- package/dist/app/review-worker.js +108 -0
- package/dist/app/review-worker.js.map +1 -0
- package/dist/app/review.d.ts.map +1 -1
- package/dist/app/review.js +14 -1
- package/dist/app/review.js.map +1 -1
- package/dist/app/server.d.ts +22 -2
- package/dist/app/server.d.ts.map +1 -1
- package/dist/app/server.js +77 -3
- package/dist/app/server.js.map +1 -1
- package/dist/app/sources.d.ts +68 -0
- package/dist/app/sources.d.ts.map +1 -0
- package/dist/app/sources.js +341 -0
- package/dist/app/sources.js.map +1 -0
- package/dist/ask/ask.d.ts +6 -0
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +30 -2
- package/dist/ask/ask.js.map +1 -1
- package/dist/blast/blast-cli.d.ts.map +1 -1
- package/dist/blast/blast-cli.js +6 -30
- package/dist/blast/blast-cli.js.map +1 -1
- package/dist/blast/name.d.ts +13 -0
- package/dist/blast/name.d.ts.map +1 -1
- package/dist/blast/name.js +36 -0
- package/dist/blast/name.js.map +1 -1
- package/dist/brain/attach.d.ts +44 -0
- package/dist/brain/attach.d.ts.map +1 -0
- package/dist/brain/attach.js +63 -0
- package/dist/brain/attach.js.map +1 -0
- package/dist/brain/connect.d.ts +59 -0
- package/dist/brain/connect.d.ts.map +1 -0
- package/dist/brain/connect.js +80 -0
- package/dist/brain/connect.js.map +1 -0
- package/dist/brain/link.d.ts +82 -0
- package/dist/brain/link.d.ts.map +1 -0
- package/dist/brain/link.js +136 -0
- package/dist/brain/link.js.map +1 -0
- package/dist/brain/push.d.ts +72 -0
- package/dist/brain/push.d.ts.map +1 -0
- package/dist/brain/push.js +191 -0
- package/dist/brain/push.js.map +1 -0
- package/dist/brain/wire.d.ts +38 -0
- package/dist/brain/wire.d.ts.map +1 -0
- package/dist/brain/wire.js +133 -0
- package/dist/brain/wire.js.map +1 -0
- package/dist/claude/format.d.ts.map +1 -1
- package/dist/claude/format.js +10 -3
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +125 -24
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts +6 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +10 -2
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/session-metrics.d.ts +73 -0
- package/dist/claude/session-metrics.d.ts.map +1 -0
- package/dist/claude/session-metrics.js +206 -0
- package/dist/claude/session-metrics.js.map +1 -0
- package/dist/claude/settings-merge.d.ts +25 -1
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +88 -25
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/skill-template.d.ts.map +1 -1
- package/dist/claude/skill-template.js +6 -0
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/claude/state.d.ts +26 -0
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js +14 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/tally.d.ts +27 -0
- package/dist/claude/tally.d.ts.map +1 -1
- package/dist/claude/tally.js +74 -1
- package/dist/claude/tally.js.map +1 -1
- package/dist/cli.js +237 -4
- package/dist/cli.js.map +1 -1
- package/dist/context/build.d.ts +11 -0
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +27 -6
- package/dist/context/build.js.map +1 -1
- package/dist/context/check.d.ts.map +1 -1
- package/dist/context/check.js +5 -13
- package/dist/context/check.js.map +1 -1
- package/dist/context/node-file.d.ts +1 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +38 -6
- package/dist/context/node-file.js.map +1 -1
- package/dist/context/price.d.ts +49 -0
- package/dist/context/price.d.ts.map +1 -0
- package/dist/context/price.js +83 -0
- package/dist/context/price.js.map +1 -0
- package/dist/context/savings.d.ts +20 -2
- package/dist/context/savings.d.ts.map +1 -1
- package/dist/context/savings.js +57 -4
- package/dist/context/savings.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/cards.d.ts +4 -3
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +34 -6
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/enrich.d.ts +4 -4
- package/dist/graph/enrich.d.ts.map +1 -1
- package/dist/graph/enrich.js +32 -6
- package/dist/graph/enrich.js.map +1 -1
- package/dist/graph/generic.d.ts +10 -0
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +20 -2
- package/dist/graph/generic.js.map +1 -1
- package/dist/graph/workspace.d.ts +2 -0
- package/dist/graph/workspace.d.ts.map +1 -1
- package/dist/graph/workspace.js +173 -6
- package/dist/graph/workspace.js.map +1 -1
- package/dist/hosts/antigravity.d.ts +2 -2
- package/dist/hosts/antigravity.d.ts.map +1 -1
- package/dist/hosts/claude-global.d.ts +33 -0
- package/dist/hosts/claude-global.d.ts.map +1 -0
- package/dist/hosts/claude-global.js +118 -0
- package/dist/hosts/claude-global.js.map +1 -0
- package/dist/hosts/codex-hooks.d.ts +2 -6
- package/dist/hosts/codex-hooks.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.js +7 -30
- package/dist/hosts/codex-hooks.js.map +1 -1
- package/dist/hosts/config-write.d.ts +36 -0
- package/dist/hosts/config-write.d.ts.map +1 -0
- package/dist/hosts/config-write.js +63 -0
- package/dist/hosts/config-write.js.map +1 -0
- package/dist/hosts/cursor-hooks.d.ts +18 -0
- package/dist/hosts/cursor-hooks.d.ts.map +1 -0
- package/dist/hosts/cursor-hooks.js +100 -0
- package/dist/hosts/cursor-hooks.js.map +1 -0
- package/dist/hosts/init.d.ts +2 -2
- package/dist/hosts/init.d.ts.map +1 -1
- package/dist/hosts/init.js +8 -2
- package/dist/hosts/init.js.map +1 -1
- package/dist/hosts/mcp-config.d.ts +3 -5
- package/dist/hosts/mcp-config.d.ts.map +1 -1
- package/dist/hosts/mcp-config.js +5 -10
- package/dist/hosts/mcp-config.js.map +1 -1
- package/dist/hosts/plan.d.ts.map +1 -1
- package/dist/hosts/plan.js +7 -1
- package/dist/hosts/plan.js.map +1 -1
- package/dist/hosts/retract.d.ts.map +1 -1
- package/dist/hosts/retract.js +33 -13
- package/dist/hosts/retract.js.map +1 -1
- package/dist/hosts/sections.d.ts +23 -2
- package/dist/hosts/sections.d.ts.map +1 -1
- package/dist/hosts/sections.js +25 -9
- package/dist/hosts/sections.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/fs.d.ts +20 -0
- package/dist/ingest/fs.d.ts.map +1 -1
- package/dist/ingest/fs.js +76 -3
- package/dist/ingest/fs.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +33 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tool-names.d.ts +20 -0
- package/dist/mcp/tool-names.d.ts.map +1 -0
- package/dist/mcp/tool-names.js +36 -0
- package/dist/mcp/tool-names.js.map +1 -0
- package/dist/mcp/tools.d.ts +11 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +14 -22
- package/dist/mcp/tools.js.map +1 -1
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +10 -1
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/flush.d.ts.map +1 -1
- package/dist/telemetry/flush.js +1 -0
- package/dist/telemetry/flush.js.map +1 -1
- package/dist/telemetry/gate.d.ts +14 -0
- package/dist/telemetry/gate.d.ts.map +1 -1
- package/dist/telemetry/gate.js +18 -2
- package/dist/telemetry/gate.js.map +1 -1
- package/dist/telemetry/identity.d.ts +7 -0
- package/dist/telemetry/identity.d.ts.map +1 -1
- package/dist/telemetry/identity.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/sessions.d.ts +19 -2
- package/dist/telemetry/sessions.d.ts.map +1 -1
- package/dist/telemetry/sessions.js +57 -40
- package/dist/telemetry/sessions.js.map +1 -1
- package/dist/telemetry/track.d.ts +22 -3
- package/dist/telemetry/track.d.ts.map +1 -1
- package/dist/telemetry/track.js +34 -5
- package/dist/telemetry/track.js.map +1 -1
- package/dist/upkeep-run.d.ts.map +1 -1
- package/dist/upkeep-run.js +12 -2
- package/dist/upkeep-run.js.map +1 -1
- package/dist/upkeep.d.ts +25 -4
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +40 -1
- package/dist/upkeep.js.map +1 -1
- package/dist/util/state.d.ts +9 -0
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js.map +1 -1
- package/package.json +2 -2
- package/scripts/postinstall.mjs +42 -3
- 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"}
|