@nanonets/graft 0.18.0 → 0.20.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 +10 -5
- package/dist/app/brain-build-process.d.ts +22 -0
- package/dist/app/brain-build-process.d.ts.map +1 -0
- package/dist/app/brain-build-process.js +129 -0
- package/dist/app/brain-build-process.js.map +1 -0
- package/dist/app/brain-build-worker.d.ts +35 -0
- package/dist/app/brain-build-worker.d.ts.map +1 -0
- package/dist/app/brain-build-worker.js +56 -0
- package/dist/app/brain-build-worker.js.map +1 -0
- package/dist/app/brain-build.d.ts +97 -3
- package/dist/app/brain-build.d.ts.map +1 -1
- package/dist/app/brain-build.js +148 -39
- package/dist/app/brain-build.js.map +1 -1
- package/dist/app/history.d.ts +2 -2
- package/dist/app/history.d.ts.map +1 -1
- package/dist/app/history.js +9 -6
- package/dist/app/history.js.map +1 -1
- package/dist/app/identity.d.ts +23 -0
- package/dist/app/identity.d.ts.map +1 -1
- package/dist/app/identity.js +47 -0
- package/dist/app/identity.js.map +1 -1
- package/dist/app/main.js +2 -0
- package/dist/app/main.js.map +1 -1
- package/dist/app/server.d.ts +10 -1
- package/dist/app/server.d.ts.map +1 -1
- package/dist/app/server.js +55 -2
- package/dist/app/server.js.map +1 -1
- package/dist/app/sources.d.ts +1 -1
- package/dist/app/sources.d.ts.map +1 -1
- package/dist/app/sources.js +3 -2
- package/dist/app/sources.js.map +1 -1
- package/dist/ask/ask.js +1 -1
- package/dist/brain/claude-md.d.ts +59 -0
- package/dist/brain/claude-md.d.ts.map +1 -0
- package/dist/brain/claude-md.js +170 -0
- package/dist/brain/claude-md.js.map +1 -0
- package/dist/brain/connect.d.ts +3 -3
- package/dist/brain/connect.js +7 -7
- package/dist/brain/link.d.ts +8 -1
- package/dist/brain/link.d.ts.map +1 -1
- package/dist/brain/link.js +11 -1
- package/dist/brain/link.js.map +1 -1
- package/dist/brain/push.d.ts +29 -1
- package/dist/brain/push.d.ts.map +1 -1
- package/dist/brain/push.js +80 -2
- package/dist/brain/push.js.map +1 -1
- package/dist/brain/signup.d.ts +73 -0
- package/dist/brain/signup.d.ts.map +1 -0
- package/dist/brain/signup.js +186 -0
- package/dist/brain/signup.js.map +1 -0
- package/dist/brain/watch.d.ts +136 -0
- package/dist/brain/watch.d.ts.map +1 -0
- package/dist/brain/watch.js +172 -0
- package/dist/brain/watch.js.map +1 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +3 -1
- package/dist/claude/init.js.map +1 -1
- package/dist/cli.js +236 -33
- package/dist/cli.js.map +1 -1
- package/dist/context/node-file.d.ts +4 -10
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +6 -2
- package/dist/context/node-file.js.map +1 -1
- package/dist/legacy-args.d.ts +12 -0
- package/dist/legacy-args.d.ts.map +1 -0
- package/dist/legacy-args.js +31 -0
- package/dist/legacy-args.js.map +1 -0
- package/dist/telemetry/contract.d.ts +12 -0
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +19 -0
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/upkeep.d.ts +1 -1
- package/dist/upkeep.js +1 -1
- package/package.json +1 -1
package/dist/brain/link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/brain/link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1G,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/brain/link.ts"],"names":[],"mappings":"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"}
|
package/dist/brain/push.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RepoDigest } from "../app/history.js";
|
|
1
|
+
import { type HistoryThread, type RepoDigest } from "../app/history.js";
|
|
2
2
|
import type { GraphV1 } from "../graph/types.js";
|
|
3
3
|
import { type BrainLink } from "./link.js";
|
|
4
4
|
/** What a local ingest read, for the caller to print. */
|
|
@@ -37,6 +37,8 @@ export interface ExpectedRepo {
|
|
|
37
37
|
slug: string;
|
|
38
38
|
status: string;
|
|
39
39
|
brainName: string;
|
|
40
|
+
/** The brain accepts an early upload (see pushEarlyDigest). */
|
|
41
|
+
earlyUpload: boolean;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
44
|
* The repository this brain expects, or null when it expects none.
|
|
@@ -53,10 +55,36 @@ export interface ExpectedRepo {
|
|
|
53
55
|
export declare function fetchExpectedRepo(link: BrainLink, fetchImpl?: typeof fetch): Promise<ExpectedRepo | null>;
|
|
54
56
|
/** Whether two repo slugs name the same repository. GitHub is case-insensitive. */
|
|
55
57
|
export declare function sameRepo(a: string, b: string): boolean;
|
|
58
|
+
/** How many of the newest pull requests and commits the early upload carries —
|
|
59
|
+
* what Trail's quick CLAUDE.md pass reads, and no more. */
|
|
60
|
+
export declare const EARLY_THREADS = 30;
|
|
61
|
+
export declare const EARLY_COMMITS = 80;
|
|
62
|
+
/**
|
|
63
|
+
* The early upload: the instruction files, the newest pull requests and the
|
|
64
|
+
* newest commits, read before the rest of the history.
|
|
65
|
+
*
|
|
66
|
+
* Reading the history is most of a push's wait — up to 200 pull requests at two
|
|
67
|
+
* GitHub calls each — and Trail's first CLAUDE.md suggestions need none of it
|
|
68
|
+
* beyond the newest few. Sent first, they start while the full read is still
|
|
69
|
+
* going here, and the full upload that follows mines as before.
|
|
70
|
+
*
|
|
71
|
+
* Returns the threads it read, so the full read reuses them instead of asking
|
|
72
|
+
* GitHub twice. Null when there is nothing worth sending early.
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildEarlyDigest(root: string, opts?: {
|
|
75
|
+
fetchImpl?: typeof fetch;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
digest: RepoDigest;
|
|
78
|
+
threads: HistoryThread[];
|
|
79
|
+
} | null>;
|
|
80
|
+
/** Send the early upload. Best-effort: false on any failure, and the full
|
|
81
|
+
* upload still does everything. */
|
|
82
|
+
export declare function pushEarlyDigest(link: BrainLink, digest: RepoDigest, fetchImpl?: typeof fetch): Promise<boolean>;
|
|
56
83
|
/** Build the digest for the checkout at `root`. Reads nothing but text. */
|
|
57
84
|
export declare function buildLocalDigest(root: string, graph: GraphV1 | null, opts?: {
|
|
58
85
|
autoApprove?: boolean;
|
|
59
86
|
fetchImpl?: typeof fetch;
|
|
87
|
+
knownThreads?: HistoryThread[];
|
|
60
88
|
}): Promise<{
|
|
61
89
|
digest: RepoDigest;
|
|
62
90
|
warning?: string;
|
package/dist/brain/push.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/brain/push.ts"],"names":[],"mappings":"AAiBA,OAAO,EAKL,KAAK,aAAa,EAClB,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;IAClB,+DAA+D;IAC/D,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAsBtH;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;2DAC2D;AAC3D,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACtC,OAAO,CAAC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,aAAa,EAAE,CAAA;CAAE,GAAG,IAAI,CAAC,CA6BlE;AAED;mCACmC;AACnC,wBAAsB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAkB5H;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,CAAC;IAAC,YAAY,CAAC,EAAE,aAAa,EAAE,CAAA;CAAO,GAC7F,OAAO,CAAC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDvE;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"}
|
package/dist/brain/push.js
CHANGED
|
@@ -104,7 +104,12 @@ export async function fetchExpectedRepo(link, fetchImpl = fetch) {
|
|
|
104
104
|
const body = (await res.json());
|
|
105
105
|
if (!body.repo?.slug)
|
|
106
106
|
return null;
|
|
107
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
slug: body.repo.slug,
|
|
109
|
+
status: String(body.repo.status ?? ""),
|
|
110
|
+
brainName: String(body.brain_name ?? ""),
|
|
111
|
+
earlyUpload: body.early_upload === true,
|
|
112
|
+
};
|
|
108
113
|
}
|
|
109
114
|
catch {
|
|
110
115
|
return null;
|
|
@@ -114,6 +119,78 @@ export async function fetchExpectedRepo(link, fetchImpl = fetch) {
|
|
|
114
119
|
export function sameRepo(a, b) {
|
|
115
120
|
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
116
121
|
}
|
|
122
|
+
/** How many of the newest pull requests and commits the early upload carries —
|
|
123
|
+
* what Trail's quick CLAUDE.md pass reads, and no more. */
|
|
124
|
+
export const EARLY_THREADS = 30;
|
|
125
|
+
export const EARLY_COMMITS = 80;
|
|
126
|
+
/**
|
|
127
|
+
* The early upload: the instruction files, the newest pull requests and the
|
|
128
|
+
* newest commits, read before the rest of the history.
|
|
129
|
+
*
|
|
130
|
+
* Reading the history is most of a push's wait — up to 200 pull requests at two
|
|
131
|
+
* GitHub calls each — and Trail's first CLAUDE.md suggestions need none of it
|
|
132
|
+
* beyond the newest few. Sent first, they start while the full read is still
|
|
133
|
+
* going here, and the full upload that follows mines as before.
|
|
134
|
+
*
|
|
135
|
+
* Returns the threads it read, so the full read reuses them instead of asking
|
|
136
|
+
* GitHub twice. Null when there is nothing worth sending early.
|
|
137
|
+
*/
|
|
138
|
+
export async function buildEarlyDigest(root, opts = {}) {
|
|
139
|
+
const slug = repoSlugFromGit(root);
|
|
140
|
+
if (!slug)
|
|
141
|
+
return null;
|
|
142
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
143
|
+
const token = githubToken();
|
|
144
|
+
// Oldest first, like the full read, so the newest are the tail.
|
|
145
|
+
const commits = readCommits(root).slice(-EARLY_COMMITS);
|
|
146
|
+
if (commits.length === 0)
|
|
147
|
+
return null;
|
|
148
|
+
let threads = [];
|
|
149
|
+
if (token) {
|
|
150
|
+
try {
|
|
151
|
+
threads = await readThreads(slug.owner, slug.name, token, fetchImpl, undefined, EARLY_THREADS);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
threads = [];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const digest = buildDigest({
|
|
158
|
+
owner: slug.owner,
|
|
159
|
+
name: slug.name,
|
|
160
|
+
headSha: git(root, ["rev-parse", "HEAD"]) ?? "",
|
|
161
|
+
defaultBranch: currentBranch(root),
|
|
162
|
+
isPrivate: await isPrivate(slug.owner, slug.name, token, fetchImpl),
|
|
163
|
+
commits,
|
|
164
|
+
threads,
|
|
165
|
+
symbols: [],
|
|
166
|
+
sources: budgetSources(readAgentInstructions(root)),
|
|
167
|
+
autoApprove: true,
|
|
168
|
+
});
|
|
169
|
+
return { digest, threads };
|
|
170
|
+
}
|
|
171
|
+
/** Send the early upload. Best-effort: false on any failure, and the full
|
|
172
|
+
* upload still does everything. */
|
|
173
|
+
export async function pushEarlyDigest(link, digest, fetchImpl = fetch) {
|
|
174
|
+
const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/repo?stage=early`;
|
|
175
|
+
try {
|
|
176
|
+
const res = await fetchImpl(url, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: {
|
|
179
|
+
authorization: `Bearer ${link.token}`,
|
|
180
|
+
"content-type": "application/json",
|
|
181
|
+
accept: "application/json",
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify(digest),
|
|
184
|
+
signal: AbortSignal.timeout(15_000),
|
|
185
|
+
});
|
|
186
|
+
if (!res.ok)
|
|
187
|
+
return false;
|
|
188
|
+
return (await res.json()).early === true;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
117
194
|
/** Build the digest for the checkout at `root`. Reads nothing but text. */
|
|
118
195
|
export async function buildLocalDigest(root, graph, opts = {}) {
|
|
119
196
|
const slug = repoSlugFromGit(root);
|
|
@@ -131,7 +208,8 @@ export async function buildLocalDigest(root, graph, opts = {}) {
|
|
|
131
208
|
let warning;
|
|
132
209
|
if (token) {
|
|
133
210
|
try {
|
|
134
|
-
|
|
211
|
+
const known = new Map((opts.knownThreads ?? []).map((t) => [t.number, t]));
|
|
212
|
+
threads = await readThreads(slug.owner, slug.name, token, fetchImpl, undefined, undefined, known);
|
|
135
213
|
}
|
|
136
214
|
catch {
|
|
137
215
|
warning = "could not read pull-request discussion; mining commits and repo files only";
|
package/dist/brain/push.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/brain/push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/brain/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { BrainLink } from "./link.js";
|
|
2
|
+
/** How long the listener waits for the browser before giving up. A signup can
|
|
3
|
+
* involve reading an email, so this is minutes rather than seconds. */
|
|
4
|
+
export declare const HANDOFF_TIMEOUT_MS: number;
|
|
5
|
+
/** The one path the listener answers on. */
|
|
6
|
+
export declare const CALLBACK_PATH = "/graft/callback";
|
|
7
|
+
/**
|
|
8
|
+
* What the browser sends back, or why it could not be accepted.
|
|
9
|
+
*
|
|
10
|
+
* `error` is the sentence printed to the user and `reason` is the same fact as a
|
|
11
|
+
* category. They are separate because the sentence names the repo and the link,
|
|
12
|
+
* so it can never be the thing telemetry reports — and matching on its text to
|
|
13
|
+
* recover the category would put a user-facing string in a position where
|
|
14
|
+
* rewording it silently changes what gets counted.
|
|
15
|
+
*/
|
|
16
|
+
export type HandoffFailure = "timed_out" | "bad_callback";
|
|
17
|
+
export type HandoffResult = {
|
|
18
|
+
link: BrainLink;
|
|
19
|
+
} | {
|
|
20
|
+
error: string;
|
|
21
|
+
reason: HandoffFailure;
|
|
22
|
+
};
|
|
23
|
+
export interface Handoff {
|
|
24
|
+
/** The loopback port Trail must redirect to. */
|
|
25
|
+
port: number;
|
|
26
|
+
/** The nonce Trail must echo back. */
|
|
27
|
+
state: string;
|
|
28
|
+
/** Resolves once the browser answers, or when `timeoutMs` passes. */
|
|
29
|
+
wait(timeoutMs?: number): Promise<HandoffResult>;
|
|
30
|
+
/** Stop listening. Safe to call more than once. */
|
|
31
|
+
close(): void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Start listening for the browser's handoff.
|
|
35
|
+
*
|
|
36
|
+
* Port 0 so the OS picks a free one: a fixed port would collide with whatever
|
|
37
|
+
* else the developer is running, and a second graft in another terminal.
|
|
38
|
+
*/
|
|
39
|
+
export declare function startHandoff(): Promise<Handoff>;
|
|
40
|
+
/** The Trail front end this machine is pointed at, without a trailing slash.
|
|
41
|
+
*
|
|
42
|
+
* Read at the moment it is needed rather than captured once, matching the rest
|
|
43
|
+
* of the brain code: GRAFT_BRAIN_URL is free to move between calls. */
|
|
44
|
+
export declare function webBaseUrl(baseUrl?: string): string;
|
|
45
|
+
/** Where the browser is sent once the handoff has been accepted.
|
|
46
|
+
*
|
|
47
|
+
* Back into Trail, at the screen that shows the brain being built. The
|
|
48
|
+
* alternative — leaving the person on a local page that says "go back to your
|
|
49
|
+
* terminal" — ends the flow on a blank throwaway served by a port that is about
|
|
50
|
+
* to close, at exactly the moment there is something to watch.
|
|
51
|
+
*
|
|
52
|
+
* NOT `/brain/<id>`, which is where this pointed first. That route redirects to
|
|
53
|
+
* the brain's graph, and the redirect fires the instant the row exists — which
|
|
54
|
+
* is minutes before it has any rules in it. Every terminal signup therefore
|
|
55
|
+
* landed on an empty visualisation of a brain that was, at that moment, being
|
|
56
|
+
* built perfectly well. The build screen is the same wait the browser-first
|
|
57
|
+
* flow shows, and it leads to the graph once there is a graph. */
|
|
58
|
+
export declare function brainUrl(brainId: string, baseUrl?: string): string;
|
|
59
|
+
/** Where to send the browser for a repo's brain. */
|
|
60
|
+
export declare function signupUrl(opts: {
|
|
61
|
+
repo: string;
|
|
62
|
+
port: number;
|
|
63
|
+
state: string;
|
|
64
|
+
baseUrl?: string;
|
|
65
|
+
}): string;
|
|
66
|
+
/** Open the user's browser, best effort. A machine with no opener is not an
|
|
67
|
+
* error: the URL is printed too, and that is the fallback everywhere.
|
|
68
|
+
*
|
|
69
|
+
* GRAFT_NO_BROWSER leaves the printed URL as the only route, which is what a
|
|
70
|
+
* test wants, and what someone on a machine whose `open` does something
|
|
71
|
+
* surprising wants too. */
|
|
72
|
+
export declare function openBrowser(url: string): void;
|
|
73
|
+
//# sourceMappingURL=signup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup.d.ts","sourceRoot":"","sources":["../../src/brain/signup.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAU3C;uEACuE;AACvE,eAAO,MAAM,kBAAkB,QAAgB,CAAC;AAEhD,4CAA4C;AAC5C,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,cAAc,CAAC;AAC1D,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,OAAO;IACtB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACjD,mDAAmD;IACnD,KAAK,IAAI,IAAI,CAAC;CACf;AAoBD;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAkFrD;AAED;;;uEAGuE;AACvE,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;;;;;;;kEAYkE;AAClE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,oDAAoD;AACpD,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CASvG;AAED;;;;;2BAK2B;AAC3B,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAQ7C"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Getting a brain without leaving the terminal.
|
|
3
|
+
*
|
|
4
|
+
* Until now a brain could only begin in the browser. You signed up on Trail, it
|
|
5
|
+
* created the brain, and handed you a `graft init --trail <id>:<token>` line to
|
|
6
|
+
* paste. That is the right way round when the website is where you already are,
|
|
7
|
+
* and the wrong way round when you are standing in a repository with graft
|
|
8
|
+
* already installed — which is where most people meet graft first.
|
|
9
|
+
*
|
|
10
|
+
* So `graft trail push` on an unlinked repo starts here instead of stopping.
|
|
11
|
+
* Graft opens a listener on loopback, sends the browser to Trail carrying the
|
|
12
|
+
* repository it is standing in and the port to answer on, and waits. Trail does
|
|
13
|
+
* the signing up, makes a brain for that repository, and redirects back to the
|
|
14
|
+
* listener with the brain id and its read token. The push then carries on as if
|
|
15
|
+
* the repo had been linked all along.
|
|
16
|
+
*
|
|
17
|
+
* Two things keep that safe. The listener binds to 127.0.0.1, so nothing off
|
|
18
|
+
* this machine can reach it. And it accepts only a handoff echoing the random
|
|
19
|
+
* state it just generated, so some other page the user happens to have open
|
|
20
|
+
* cannot push a brain of its own choosing into their repository.
|
|
21
|
+
*/
|
|
22
|
+
import { spawn } from "node:child_process";
|
|
23
|
+
import { createServer } from "node:http";
|
|
24
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
25
|
+
/** Default web host. Overridden by GRAFT_BRAIN_URL, for staging and self-hosted.
|
|
26
|
+
*
|
|
27
|
+
* Trail's own front end, not the shared `agents.nanonets.com` one that `link.ts`
|
|
28
|
+
* calls for the API. Both are served by the same backend, so either would mint a
|
|
29
|
+
* working token — but this URL is the one a person looks at, and it has to be
|
|
30
|
+
* the Trail-branded build, with Trail's Auth0 redirect behind the signup. */
|
|
31
|
+
const DEFAULT_WEB_BASE_URL = "https://app.trailhq.com";
|
|
32
|
+
/** How long the listener waits for the browser before giving up. A signup can
|
|
33
|
+
* involve reading an email, so this is minutes rather than seconds. */
|
|
34
|
+
export const HANDOFF_TIMEOUT_MS = 5 * 60 * 1000;
|
|
35
|
+
/** The one path the listener answers on. */
|
|
36
|
+
export const CALLBACK_PATH = "/graft/callback";
|
|
37
|
+
/** Constant-time compare of two states, length included. */
|
|
38
|
+
function sameState(got, want) {
|
|
39
|
+
const a = Buffer.from(got);
|
|
40
|
+
const b = Buffer.from(want);
|
|
41
|
+
if (a.length !== b.length)
|
|
42
|
+
return false;
|
|
43
|
+
return timingSafeEqual(a, b);
|
|
44
|
+
}
|
|
45
|
+
/** The page the user is left looking at. Deliberately plain: it exists to say
|
|
46
|
+
* "go back to your terminal", and it is served from a throwaway port that is
|
|
47
|
+
* about to close, so there is nothing to style around. */
|
|
48
|
+
function donePage(ok) {
|
|
49
|
+
const msg = ok
|
|
50
|
+
? "Your brain is connected. Return to your terminal — the push is already running."
|
|
51
|
+
: "That handoff did not match this terminal. Run `graft trail push` again.";
|
|
52
|
+
return `<!doctype html><meta charset="utf-8"><title>graft</title><body style="font:15px/1.5 system-ui,sans-serif;margin:3rem auto;max-width:32rem;color:#1F2129"><p>${msg}</p><p style="color:#676767">You can close this tab.</p>`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Start listening for the browser's handoff.
|
|
56
|
+
*
|
|
57
|
+
* Port 0 so the OS picks a free one: a fixed port would collide with whatever
|
|
58
|
+
* else the developer is running, and a second graft in another terminal.
|
|
59
|
+
*/
|
|
60
|
+
export async function startHandoff() {
|
|
61
|
+
const state = randomBytes(24).toString("base64url");
|
|
62
|
+
let settle = () => { };
|
|
63
|
+
const answered = new Promise((resolve) => {
|
|
64
|
+
settle = resolve;
|
|
65
|
+
});
|
|
66
|
+
const server = createServer((req, res) => {
|
|
67
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
68
|
+
if (url.pathname !== CALLBACK_PATH) {
|
|
69
|
+
res.writeHead(404, { "content-type": "text/plain" });
|
|
70
|
+
res.end("not found");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Checked before anything is read out of the query, so a mismatched handoff
|
|
74
|
+
// never reaches the link-writing path at all.
|
|
75
|
+
if (!sameState(url.searchParams.get("state") ?? "", state)) {
|
|
76
|
+
res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
77
|
+
res.end(donePage(false));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const brainId = (url.searchParams.get("brain") ?? "").trim();
|
|
81
|
+
const token = (url.searchParams.get("token") ?? "").trim();
|
|
82
|
+
if (!brainId || !token) {
|
|
83
|
+
res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
84
|
+
res.end(donePage(false));
|
|
85
|
+
settle({ error: "the browser came back without a brain id and token", reason: "bad_callback" });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// 303 so the browser issues a plain GET, and Location built from
|
|
89
|
+
// GRAFT_BRAIN_URL rather than anything the query carried — the same reason
|
|
90
|
+
// the Trail side builds its callback rather than being told one.
|
|
91
|
+
res.writeHead(303, { location: brainUrl(brainId), "cache-control": "no-store" });
|
|
92
|
+
res.end();
|
|
93
|
+
// No `baseUrl` stored, matching `connect`: GRAFT_BRAIN_URL is read again on
|
|
94
|
+
// every use, so persisting it here would only freeze a host that the env
|
|
95
|
+
// var is already free to move.
|
|
96
|
+
settle({ link: { brainId, token } });
|
|
97
|
+
});
|
|
98
|
+
await new Promise((resolve, reject) => {
|
|
99
|
+
server.once("error", reject);
|
|
100
|
+
server.listen(0, "127.0.0.1", () => {
|
|
101
|
+
server.removeAllListeners("error");
|
|
102
|
+
resolve();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
const port = server.address().port;
|
|
106
|
+
let closed = false;
|
|
107
|
+
const close = () => {
|
|
108
|
+
if (closed)
|
|
109
|
+
return;
|
|
110
|
+
closed = true;
|
|
111
|
+
server.close();
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
port,
|
|
115
|
+
state,
|
|
116
|
+
close,
|
|
117
|
+
async wait(timeoutMs = HANDOFF_TIMEOUT_MS) {
|
|
118
|
+
let timer;
|
|
119
|
+
const timeout = new Promise((resolve) => {
|
|
120
|
+
timer = setTimeout(() => resolve({ error: "timed out waiting for the browser — run `graft trail push` again, or use the link above", reason: "timed_out" }), timeoutMs);
|
|
121
|
+
// The timer must not hold the process open once the browser has answered.
|
|
122
|
+
timer.unref?.();
|
|
123
|
+
});
|
|
124
|
+
try {
|
|
125
|
+
return await Promise.race([answered, timeout]);
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
if (timer)
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
close();
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** The Trail front end this machine is pointed at, without a trailing slash.
|
|
136
|
+
*
|
|
137
|
+
* Read at the moment it is needed rather than captured once, matching the rest
|
|
138
|
+
* of the brain code: GRAFT_BRAIN_URL is free to move between calls. */
|
|
139
|
+
export function webBaseUrl(baseUrl) {
|
|
140
|
+
return (process.env.GRAFT_BRAIN_URL || baseUrl || DEFAULT_WEB_BASE_URL).replace(/\/+$/, "");
|
|
141
|
+
}
|
|
142
|
+
/** Where the browser is sent once the handoff has been accepted.
|
|
143
|
+
*
|
|
144
|
+
* Back into Trail, at the screen that shows the brain being built. The
|
|
145
|
+
* alternative — leaving the person on a local page that says "go back to your
|
|
146
|
+
* terminal" — ends the flow on a blank throwaway served by a port that is about
|
|
147
|
+
* to close, at exactly the moment there is something to watch.
|
|
148
|
+
*
|
|
149
|
+
* NOT `/brain/<id>`, which is where this pointed first. That route redirects to
|
|
150
|
+
* the brain's graph, and the redirect fires the instant the row exists — which
|
|
151
|
+
* is minutes before it has any rules in it. Every terminal signup therefore
|
|
152
|
+
* landed on an empty visualisation of a brain that was, at that moment, being
|
|
153
|
+
* built perfectly well. The build screen is the same wait the browser-first
|
|
154
|
+
* flow shows, and it leads to the graph once there is a graph. */
|
|
155
|
+
export function brainUrl(brainId, baseUrl) {
|
|
156
|
+
return `${webBaseUrl(baseUrl)}/get-started?step=build&brain=${encodeURIComponent(brainId)}`;
|
|
157
|
+
}
|
|
158
|
+
/** Where to send the browser for a repo's brain. */
|
|
159
|
+
export function signupUrl(opts) {
|
|
160
|
+
const base = webBaseUrl(opts.baseUrl);
|
|
161
|
+
const q = new URLSearchParams({
|
|
162
|
+
step: "repo",
|
|
163
|
+
graft_repo: opts.repo,
|
|
164
|
+
graft_port: String(opts.port),
|
|
165
|
+
graft_state: opts.state,
|
|
166
|
+
});
|
|
167
|
+
return `${base}/get-started?${q.toString()}`;
|
|
168
|
+
}
|
|
169
|
+
/** Open the user's browser, best effort. A machine with no opener is not an
|
|
170
|
+
* error: the URL is printed too, and that is the fallback everywhere.
|
|
171
|
+
*
|
|
172
|
+
* GRAFT_NO_BROWSER leaves the printed URL as the only route, which is what a
|
|
173
|
+
* test wants, and what someone on a machine whose `open` does something
|
|
174
|
+
* surprising wants too. */
|
|
175
|
+
export function openBrowser(url) {
|
|
176
|
+
if (process.env.GRAFT_NO_BROWSER)
|
|
177
|
+
return;
|
|
178
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
179
|
+
try {
|
|
180
|
+
spawn(opener, [url], { stdio: "ignore", detached: true, shell: process.platform === "win32" }).unref();
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
/* the printed URL is the fallback */
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=signup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup.js","sourceRoot":"","sources":["../../src/brain/signup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watching a brain being built, from the terminal.
|
|
3
|
+
*
|
|
4
|
+
* `graft trail push` used to end at "it is being mined into rules now — a few
|
|
5
|
+
* minutes. Watch it finish in your browser." and hand the prompt straight back.
|
|
6
|
+
* That sentence is the last thing this process ever says about the work, and it
|
|
7
|
+
* is said BEFORE the part that actually fails: of the repo brains attempted in
|
|
8
|
+
* production, roughly a third die inside the miner, after the push succeeded.
|
|
9
|
+
* The terminal has already returned by then, so on this side the failure does
|
|
10
|
+
* not exist at all — and on a CI runner or over SSH, where nobody is going to
|
|
11
|
+
* open a browser, it exists nowhere.
|
|
12
|
+
*
|
|
13
|
+
* So the push now holds the line and reports the same four stages the browser
|
|
14
|
+
* shows, off the same row. Ctrl-C detaches without cancelling anything, because
|
|
15
|
+
* the work is server-side and killing a watcher must never look like killing
|
|
16
|
+
* the build.
|
|
17
|
+
*
|
|
18
|
+
* What it holds the line UNTIL changed with slicing. The history is now mined in
|
|
19
|
+
* several calls rather than one, and the first of them returns in about ten
|
|
20
|
+
* seconds; filing all of the rules into the graph takes minutes more. Mining is
|
|
21
|
+
* where the failures are — the thing this watcher exists to report — and filing
|
|
22
|
+
* is long, dull and reliable, so the line is held through the first and handed
|
|
23
|
+
* back before the second. Holding a terminal through five minutes of successful
|
|
24
|
+
* filing buys nobody anything, and on a CI runner it is five minutes of a job
|
|
25
|
+
* sitting idle.
|
|
26
|
+
*/
|
|
27
|
+
import type { BrainLink } from "./link.js";
|
|
28
|
+
/** The stages, in the order they happen. The wording matches Trail's build
|
|
29
|
+
* screen deliberately: two products describing one process differently is how
|
|
30
|
+
* a person ends up unsure whether they are looking at the same thing. */
|
|
31
|
+
export declare const STAGES: readonly [{
|
|
32
|
+
readonly id: "reach";
|
|
33
|
+
readonly label: "reaching the repository";
|
|
34
|
+
}, {
|
|
35
|
+
readonly id: "read";
|
|
36
|
+
readonly label: "reading its history";
|
|
37
|
+
}, {
|
|
38
|
+
readonly id: "mine";
|
|
39
|
+
readonly label: "mining the rules";
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: "file";
|
|
42
|
+
readonly label: "filing them into the brain";
|
|
43
|
+
}];
|
|
44
|
+
export type StageId = (typeof STAGES)[number]["id"];
|
|
45
|
+
export type StageState = "waiting" | "doing" | "done" | "failed";
|
|
46
|
+
export interface Stage {
|
|
47
|
+
id: StageId;
|
|
48
|
+
label: string;
|
|
49
|
+
state: StageState;
|
|
50
|
+
/** A count worth printing beside a stage that has produced one. */
|
|
51
|
+
detail?: string;
|
|
52
|
+
}
|
|
53
|
+
/** The repo row, as the public endpoint returns it. Only the fields the stages
|
|
54
|
+
* read, so a field added server-side cannot quietly change what is printed. */
|
|
55
|
+
export interface RepoState {
|
|
56
|
+
status: string;
|
|
57
|
+
errorMessage?: string;
|
|
58
|
+
ruleCount: number;
|
|
59
|
+
commitCount: number;
|
|
60
|
+
threadCount: number;
|
|
61
|
+
/** Rules in the graph right now, from the job still running. The row's own
|
|
62
|
+
* `ruleCount` is only written when that job finishes, so mid-build it is
|
|
63
|
+
* zero however many rules are already there.
|
|
64
|
+
*
|
|
65
|
+
* Optional because an older Trail does not send it, and a graft pointed at
|
|
66
|
+
* one must fall back to waiting for the finished row rather than reading a
|
|
67
|
+
* missing field as "no rules". */
|
|
68
|
+
filedSoFar?: number;
|
|
69
|
+
/** Rules the miner has handed over so far. Grows with every slice of the
|
|
70
|
+
* history that comes back, so it is a floor and never a total. */
|
|
71
|
+
foundSoFar?: number;
|
|
72
|
+
}
|
|
73
|
+
export interface BuildView {
|
|
74
|
+
stages: Stage[];
|
|
75
|
+
/**
|
|
76
|
+
* True once rules are in the graph — which is when this watcher has nothing
|
|
77
|
+
* left to report, not when the job ends. The rest of the slices keep mining
|
|
78
|
+
* and filing after the prompt comes back.
|
|
79
|
+
*/
|
|
80
|
+
ready: boolean;
|
|
81
|
+
done: boolean;
|
|
82
|
+
/** The server's own words, when it failed. */
|
|
83
|
+
error: string | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Which stage the work is in.
|
|
87
|
+
*
|
|
88
|
+
* The rule that matters is where a failure lands. A row that never left
|
|
89
|
+
* `pending` failed at the access check; one that read 412 commits and then died
|
|
90
|
+
* failed in the miner. Reporting both as "could not reach it" would send someone
|
|
91
|
+
* to fix repository access for a problem that has nothing to do with it.
|
|
92
|
+
*/
|
|
93
|
+
export declare function stagesFrom(repo: RepoState | null): BuildView;
|
|
94
|
+
/**
|
|
95
|
+
* Read the repo row this brain is building.
|
|
96
|
+
*
|
|
97
|
+
* The same endpoint `fetchExpectedRepo` uses, read for its counts rather than
|
|
98
|
+
* its slug. Null on any failure, so a watcher that cannot reach the API prints
|
|
99
|
+
* nothing new rather than inventing a failure the build did not have.
|
|
100
|
+
*/
|
|
101
|
+
export declare function fetchRepoState(link: BrainLink, fetchImpl?: typeof fetch): Promise<RepoState | null>;
|
|
102
|
+
/** One printable line per stage that has moved since it was last printed. */
|
|
103
|
+
export declare function linesFor(view: BuildView, already: Set<string>): string[];
|
|
104
|
+
export interface WatchOptions {
|
|
105
|
+
/** How long to hold before giving up on a build that is still running. */
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
pollMs?: number;
|
|
108
|
+
fetchImpl?: typeof fetch;
|
|
109
|
+
/** Where the lines go. Injected so the tests do not need a console. */
|
|
110
|
+
write?: (line: string) => void;
|
|
111
|
+
sleep?: (ms: number) => Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* How the watch ended.
|
|
115
|
+
*
|
|
116
|
+
* `building` is the ordinary success: rules are in the brain and the rest of the
|
|
117
|
+
* history is still being read behind the prompt. `completed` is the same thing
|
|
118
|
+
* after the whole job has finished, which only happens for a repository small
|
|
119
|
+
* enough to be mined in one slice.
|
|
120
|
+
*/
|
|
121
|
+
export type WatchOutcome = "completed" | "building" | "failed" | "timed_out" | "unreachable";
|
|
122
|
+
/**
|
|
123
|
+
* Hold until the brain has rules, printing each stage as it settles.
|
|
124
|
+
*
|
|
125
|
+
* Returns on the first rules rather than on the finished job: mining is what
|
|
126
|
+
* fails and it is done by then, and filing the rest is minutes of work nobody
|
|
127
|
+
* needs to watch. The caller prints where to see the rest.
|
|
128
|
+
*
|
|
129
|
+
* Fifteen minutes by default. That is far more than the mining now takes, and it
|
|
130
|
+
* stays generous on purpose: a watcher that gives up before the build does would
|
|
131
|
+
* report a healthy build as a timeout, which is the exact confusion this is
|
|
132
|
+
* meant to remove. Giving up says so and says the work continues, because it
|
|
133
|
+
* does.
|
|
134
|
+
*/
|
|
135
|
+
export declare function watchBuild(link: BrainLink, opts?: WatchOptions): Promise<WatchOutcome>;
|
|
136
|
+
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/brain/watch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C;;0EAE0E;AAC1E,eAAO,MAAM,MAAM;;;;;;;;;;;;EAKT,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;gFACgF;AAChF,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;uCAMmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;uEACmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB;;;;OAIG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,8CAA8C;IAC9C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAID;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAsD5D;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA2BhH;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAcxE;AAED,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,uEAAuE;IACvE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAE7F;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAgChG"}
|