@nanonets/graft 0.20.0 → 0.21.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/dist/app/history.d.ts +36 -1
- package/dist/app/history.d.ts.map +1 -1
- package/dist/app/history.js +72 -25
- package/dist/app/history.js.map +1 -1
- package/dist/app/sources.d.ts +13 -0
- package/dist/app/sources.d.ts.map +1 -1
- package/dist/app/sources.js +129 -1
- package/dist/app/sources.js.map +1 -1
- package/dist/brain/claude-md.d.ts +4 -2
- package/dist/brain/claude-md.d.ts.map +1 -1
- package/dist/brain/claude-md.js +4 -3
- package/dist/brain/claude-md.js.map +1 -1
- package/dist/brain/context-files.d.ts +79 -0
- package/dist/brain/context-files.d.ts.map +1 -0
- package/dist/brain/context-files.js +245 -0
- package/dist/brain/context-files.js.map +1 -0
- package/dist/brain/link.d.ts +17 -0
- package/dist/brain/link.d.ts.map +1 -1
- package/dist/brain/link.js +21 -0
- package/dist/brain/link.js.map +1 -1
- package/dist/brain/pull.d.ts +17 -0
- package/dist/brain/pull.d.ts.map +1 -0
- package/dist/brain/pull.js +172 -0
- package/dist/brain/pull.js.map +1 -0
- package/dist/brain/push.d.ts +62 -7
- package/dist/brain/push.d.ts.map +1 -1
- package/dist/brain/push.js +97 -62
- package/dist/brain/push.js.map +1 -1
- package/dist/brain/signup.d.ts +46 -2
- package/dist/brain/signup.d.ts.map +1 -1
- package/dist/brain/signup.js +101 -11
- package/dist/brain/signup.js.map +1 -1
- package/dist/brain/upload.d.ts +61 -0
- package/dist/brain/upload.d.ts.map +1 -0
- package/dist/brain/upload.js +210 -0
- package/dist/brain/upload.js.map +1 -0
- package/dist/brain/watch.d.ts +76 -3
- package/dist/brain/watch.d.ts.map +1 -1
- package/dist/brain/watch.js +263 -39
- package/dist/brain/watch.js.map +1 -1
- package/dist/claude/build-progress.d.ts +29 -0
- package/dist/claude/build-progress.d.ts.map +1 -0
- package/dist/claude/build-progress.js +76 -0
- package/dist/claude/build-progress.js.map +1 -0
- package/dist/cli-picker.d.ts +17 -2
- package/dist/cli-picker.d.ts.map +1 -1
- package/dist/cli-picker.js +103 -22
- package/dist/cli-picker.js.map +1 -1
- package/dist/cli.js +423 -187
- package/dist/cli.js.map +1 -1
- package/dist/telemetry/contract.d.ts +16 -3
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +23 -5
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/util/spinner.d.ts +15 -0
- package/dist/util/spinner.d.ts.map +1 -0
- package/dist/util/spinner.js +52 -0
- package/dist/util/spinner.js.map +1 -0
- package/dist/util/state.d.ts +8 -0
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js.map +1 -1
- package/package.json +1 -1
package/dist/brain/signup.js
CHANGED
|
@@ -47,8 +47,8 @@ function sameState(got, want) {
|
|
|
47
47
|
* about to close, so there is nothing to style around. */
|
|
48
48
|
function donePage(ok) {
|
|
49
49
|
const msg = ok
|
|
50
|
-
? "Your
|
|
51
|
-
: "That
|
|
50
|
+
? "Your trail is connected. Return to your terminal — the push is already running."
|
|
51
|
+
: "That sign-up doesn't match this terminal. Run graft trail push again.";
|
|
52
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
53
|
}
|
|
54
54
|
/**
|
|
@@ -82,7 +82,7 @@ export async function startHandoff() {
|
|
|
82
82
|
if (!brainId || !token) {
|
|
83
83
|
res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
84
84
|
res.end(donePage(false));
|
|
85
|
-
settle({ error: "
|
|
85
|
+
settle({ error: "sign-up came back incomplete · nothing was sent — run graft trail push again", reason: "bad_callback" });
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
// 303 so the browser issues a plain GET, and Location built from
|
|
@@ -117,7 +117,10 @@ export async function startHandoff() {
|
|
|
117
117
|
async wait(timeoutMs = HANDOFF_TIMEOUT_MS) {
|
|
118
118
|
let timer;
|
|
119
119
|
const timeout = new Promise((resolve) => {
|
|
120
|
-
timer = setTimeout(() => resolve({
|
|
120
|
+
timer = setTimeout(() => resolve({
|
|
121
|
+
error: `no sign-up after ${timeoutMs >= 60_000 ? `${Math.round(timeoutMs / 60_000)} minutes` : `${Math.max(1, Math.round(timeoutMs / 1000))} seconds`} · nothing was sent — run graft trail push again`,
|
|
122
|
+
reason: "timed_out",
|
|
123
|
+
}), timeoutMs);
|
|
121
124
|
// The timer must not hold the process open once the browser has answered.
|
|
122
125
|
timer.unref?.();
|
|
123
126
|
});
|
|
@@ -155,17 +158,104 @@ export function webBaseUrl(baseUrl) {
|
|
|
155
158
|
export function brainUrl(brainId, baseUrl) {
|
|
156
159
|
return `${webBaseUrl(baseUrl)}/get-started?step=build&brain=${encodeURIComponent(brainId)}`;
|
|
157
160
|
}
|
|
158
|
-
/** Where
|
|
161
|
+
/** Where a trail's suggested context-file changes are reviewed and accepted:
|
|
162
|
+
* the Context files overview, which lists every file the repo's agents read
|
|
163
|
+
* (CLAUDE.md, AGENTS.md, Cursor rules, folder files, skills) with what waits
|
|
164
|
+
* on each. Needs a Trail that has the page (NanoNets/assign#2957). */
|
|
165
|
+
export function reviewUrl(brainId, baseUrl) {
|
|
166
|
+
return `${webBaseUrl(baseUrl)}/brain/${encodeURIComponent(brainId)}/context-files`;
|
|
167
|
+
}
|
|
168
|
+
/** Where to send the browser for a repo's brain.
|
|
169
|
+
*
|
|
170
|
+
* No port when an agent runs the push: nothing stays listening on loopback, so
|
|
171
|
+
* Trail keeps the trail it made under `state` and graft asks for it by that. */
|
|
159
172
|
export function signupUrl(opts) {
|
|
160
173
|
const base = webBaseUrl(opts.baseUrl);
|
|
161
|
-
const q = new URLSearchParams({
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
graft_state: opts.state,
|
|
166
|
-
});
|
|
174
|
+
const q = new URLSearchParams({ step: "repo", graft_repo: opts.repo });
|
|
175
|
+
if (opts.port !== undefined)
|
|
176
|
+
q.set("graft_port", String(opts.port));
|
|
177
|
+
q.set("graft_state", opts.state);
|
|
167
178
|
return `${base}/get-started?${q.toString()}`;
|
|
168
179
|
}
|
|
180
|
+
/*
|
|
181
|
+
* Signing up when an agent runs the push.
|
|
182
|
+
*
|
|
183
|
+
* Claude Code, Cursor and the rest run `graft trail push` with no terminal, and
|
|
184
|
+
* show the person nothing until the command exits. A loopback listener cannot
|
|
185
|
+
* work there: the link would sit unseen inside a running command, and anything
|
|
186
|
+
* the browser is sent back to is gone by the time the person finishes signing
|
|
187
|
+
* up. So the push runs in two short steps instead. The first opens the sign-up
|
|
188
|
+
* page, saves the state it put in the link, and exits at once so the agent can
|
|
189
|
+
* tell the person what to do. The next asks Trail every couple of seconds,
|
|
190
|
+
* by that state, whether the sign-up has finished, and carries on with the push
|
|
191
|
+
* when it has.
|
|
192
|
+
*/
|
|
193
|
+
/** A fresh state for a sign-up link: 24 random bytes, which is what Trail
|
|
194
|
+
* accepts and what makes the link impossible to guess. */
|
|
195
|
+
export function newSignupState() {
|
|
196
|
+
return randomBytes(24).toString("base64url");
|
|
197
|
+
}
|
|
198
|
+
/** How long one agent-run push waits for the sign-up. Under the two minutes an
|
|
199
|
+
* agent gives a command by default, so the wait ends with a message it can read
|
|
200
|
+
* rather than a killed process. */
|
|
201
|
+
export const AGENT_WAIT_MS = 90 * 1000;
|
|
202
|
+
/** How often it asks. */
|
|
203
|
+
export const AGENT_POLL_MS = 2000;
|
|
204
|
+
/** How long a saved state is worth asking about: a minute under the fifteen
|
|
205
|
+
* Trail keeps an unclaimed sign-up, so graft never waits on one Trail dropped. */
|
|
206
|
+
export const PENDING_SIGNUP_TTL_MS = 14 * 60 * 1000;
|
|
207
|
+
/**
|
|
208
|
+
* Ask Trail once whether the sign-up for `state` has finished.
|
|
209
|
+
*
|
|
210
|
+
* A network error or a server error counts as "not yet": one bad request in a
|
|
211
|
+
* ninety-second wait is not a reason to give up on it.
|
|
212
|
+
*/
|
|
213
|
+
export async function claimSignup(state, apiBase, fetchImpl = fetch) {
|
|
214
|
+
let res;
|
|
215
|
+
try {
|
|
216
|
+
res = await fetchImpl(`${apiBase}/api/public/graft-handoffs/claim`, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers: { "content-type": "application/json" },
|
|
219
|
+
body: JSON.stringify({ state }),
|
|
220
|
+
signal: AbortSignal.timeout(10_000),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return { pending: true };
|
|
225
|
+
}
|
|
226
|
+
if (res.status === 200) {
|
|
227
|
+
const body = (await res.json().catch(() => ({})));
|
|
228
|
+
if (body.brain_id && body.token)
|
|
229
|
+
return { link: { brainId: body.brain_id, token: body.token } };
|
|
230
|
+
return { pending: true };
|
|
231
|
+
}
|
|
232
|
+
await res.body?.cancel().catch(() => undefined);
|
|
233
|
+
if (res.status === 410) {
|
|
234
|
+
return { error: "that sign-up link was already used or has expired · run graft trail push again for a new one", reason: "expired" };
|
|
235
|
+
}
|
|
236
|
+
// An older Trail, without the route. Waiting on it would never end.
|
|
237
|
+
if (res.status === 404 || res.status === 405) {
|
|
238
|
+
return {
|
|
239
|
+
error: "this Trail can't hand a sign-up to an agent yet · run graft trail push in a terminal, or graft trail connect <trailId>:<token>",
|
|
240
|
+
reason: "unsupported",
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return { pending: true };
|
|
244
|
+
}
|
|
245
|
+
/** Ask until the sign-up finishes, Trail says it never will, or `timeoutMs`
|
|
246
|
+
* passes — which comes back as `{ pending: true }`. */
|
|
247
|
+
export async function waitForSignup(state, apiBase, opts = {}) {
|
|
248
|
+
const deadline = Date.now() + (opts.timeoutMs ?? AGENT_WAIT_MS);
|
|
249
|
+
const interval = opts.intervalMs ?? AGENT_POLL_MS;
|
|
250
|
+
for (;;) {
|
|
251
|
+
const got = await claimSignup(state, apiBase, opts.fetchImpl);
|
|
252
|
+
if (!("pending" in got))
|
|
253
|
+
return got;
|
|
254
|
+
if (Date.now() + interval > deadline)
|
|
255
|
+
return got;
|
|
256
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
169
259
|
/** Open the user's browser, best effort. A machine with no opener is not an
|
|
170
260
|
* error: the URL is printed too, and that is the fallback everywhere.
|
|
171
261
|
*
|
package/dist/brain/signup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup.js","sourceRoot":"","sources":["../../src/brain/signup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI3D;;;;;6EAK6E;AAC7E,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;AAEvD;uEACuE;AACvE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD,4CAA4C;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAyB/C,4DAA4D;AAC5D,SAAS,SAAS,CAAC,GAAW,EAAE,IAAY;IAC1C,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED;;0DAE0D;AAC1D,SAAS,QAAQ,CAAC,EAAW;IAC3B,MAAM,GAAG,GAAG,EAAE;QACZ,CAAC,CAAC,iFAAiF;QACnF,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"signup.js","sourceRoot":"","sources":["../../src/brain/signup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { RepoDigest } from "../app/history.js";
|
|
2
|
+
import { type BrainLink } from "./link.js";
|
|
3
|
+
/** The size of one chunk: the index-th slice of the gzip is bytes [i*CHUNK, (i+1)*CHUNK). */
|
|
4
|
+
export declare const CHUNK_BYTES: number;
|
|
5
|
+
/** Chunks in flight at once. */
|
|
6
|
+
export declare const CHUNK_CONCURRENCY = 4;
|
|
7
|
+
/** Waits before each retry. Four retries, so five attempts in all. */
|
|
8
|
+
export declare const RETRY_DELAYS_MS: readonly [500, 1000, 2000, 4000];
|
|
9
|
+
/** What the server said it accepts, from GET /repo. */
|
|
10
|
+
export interface UploadCaps {
|
|
11
|
+
/** `gzip_upload: true` — POST /repo takes `Content-Encoding: gzip`. */
|
|
12
|
+
gzip?: boolean;
|
|
13
|
+
/** `chunked_upload: true` — the /repo/uploads routes exist. */
|
|
14
|
+
chunked?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface UploadOptions {
|
|
17
|
+
fetchImpl?: typeof fetch;
|
|
18
|
+
sleep?: (ms: number) => Promise<void>;
|
|
19
|
+
chunkBytes?: number;
|
|
20
|
+
concurrency?: number;
|
|
21
|
+
}
|
|
22
|
+
export type UploadResult = {
|
|
23
|
+
jobId: string;
|
|
24
|
+
} | {
|
|
25
|
+
error: string;
|
|
26
|
+
};
|
|
27
|
+
/** The digest as the exact bytes a single POST would carry, gzipped. */
|
|
28
|
+
export declare function gzipDigest(digest: RepoDigest): Promise<Buffer>;
|
|
29
|
+
/**
|
|
30
|
+
* The single POST to /repo — gzipped when the server takes it, plain otherwise.
|
|
31
|
+
* Not retried: a repeated POST of a whole ingest is not idempotent, and a 409
|
|
32
|
+
* for "already running" on the retry would read as a refusal.
|
|
33
|
+
*/
|
|
34
|
+
export declare function postDigestOnce(link: BrainLink, digest: RepoDigest, opts?: {
|
|
35
|
+
gzip?: boolean;
|
|
36
|
+
fetchImpl?: typeof fetch;
|
|
37
|
+
query?: string;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
}): Promise<Response>;
|
|
40
|
+
/**
|
|
41
|
+
* Upload the full digest by whichever route the server advertises.
|
|
42
|
+
*
|
|
43
|
+
* Chunked when `caps.chunked`; if the upload session cannot even be opened
|
|
44
|
+
* (a 404 from a server that advertised the flag but not the route), it falls
|
|
45
|
+
* back to the single POST rather than failing a push that would otherwise work.
|
|
46
|
+
*/
|
|
47
|
+
export declare function uploadDigest(link: BrainLink, digest: RepoDigest, caps: UploadCaps, opts?: UploadOptions): Promise<UploadResult>;
|
|
48
|
+
/**
|
|
49
|
+
* The chunked route:
|
|
50
|
+
*
|
|
51
|
+
* POST /repo/uploads {stage, auto_approve, chunks, bytes} → {upload_id}
|
|
52
|
+
* PUT /repo/uploads/:id/chunks/:index raw gzip slice, idempotent → 204
|
|
53
|
+
* POST /repo/uploads/:id/complete → today's /repo response
|
|
54
|
+
*
|
|
55
|
+
* `{fallback: true}` means the session could not be opened and nothing was
|
|
56
|
+
* sent, so the caller may use the single POST instead.
|
|
57
|
+
*/
|
|
58
|
+
export declare function uploadChunked(link: BrainLink, digest: RepoDigest, opts?: UploadOptions): Promise<UploadResult | {
|
|
59
|
+
fallback: true;
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/brain/upload.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAIvD,6FAA6F;AAC7F,eAAO,MAAM,WAAW,QAAc,CAAC;AACvC,gCAAgC;AAChC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,sEAAsE;AACtE,eAAO,MAAM,eAAe,kCAAmC,CAAC;AAEhE,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAIjE,wEAAwE;AACxE,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpE;AAmDD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1F,OAAO,CAAC,QAAQ,CAAC,CAanB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,YAAY,CAAC,CAUvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,YAAY,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC,CA0F5C"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Getting a digest to Trail quickly, and getting it there at all.
|
|
3
|
+
*
|
|
4
|
+
* A busy repository's digest is several megabytes of JSON — a thousand commit
|
|
5
|
+
* messages, two hundred threads of discussion, four thousand symbols — and it
|
|
6
|
+
* used to go up as one uncompressed POST with no retry. On a hotel connection
|
|
7
|
+
* that is a minute of upload and one dropped packet from starting over.
|
|
8
|
+
*
|
|
9
|
+
* JSON compresses about tenfold, so the body is gzipped whenever the server
|
|
10
|
+
* says it takes gzip. When it also says it takes chunked uploads, the gzip is
|
|
11
|
+
* cut into 1 MiB pieces and sent four at a time, each retried on its own: a
|
|
12
|
+
* blip costs one chunk, not the push. Against an older Trail neither flag is
|
|
13
|
+
* set and the upload is exactly the single POST it always was.
|
|
14
|
+
*/
|
|
15
|
+
import { gzip as gzipCb } from "node:zlib";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
import { baseUrlFor } from "./link.js";
|
|
18
|
+
const gzipAsync = promisify(gzipCb);
|
|
19
|
+
/** The size of one chunk: the index-th slice of the gzip is bytes [i*CHUNK, (i+1)*CHUNK). */
|
|
20
|
+
export const CHUNK_BYTES = 1024 * 1024;
|
|
21
|
+
/** Chunks in flight at once. */
|
|
22
|
+
export const CHUNK_CONCURRENCY = 4;
|
|
23
|
+
/** Waits before each retry. Four retries, so five attempts in all. */
|
|
24
|
+
export const RETRY_DELAYS_MS = [500, 1000, 2000, 4000];
|
|
25
|
+
const realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26
|
+
/** The digest as the exact bytes a single POST would carry, gzipped. */
|
|
27
|
+
export async function gzipDigest(digest) {
|
|
28
|
+
return gzipAsync(Buffer.from(JSON.stringify(digest), "utf8"));
|
|
29
|
+
}
|
|
30
|
+
/** Retry on what a retry can fix: the network, the server, the rate limit. */
|
|
31
|
+
function retryable(status) {
|
|
32
|
+
return status >= 500 || status === 429;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One request, retried with backoff on network errors, 5xx and 429.
|
|
36
|
+
*
|
|
37
|
+
* Returns the last response when retries run out on a status (so the caller
|
|
38
|
+
* can print the server's own words), or throws the last network error.
|
|
39
|
+
*/
|
|
40
|
+
async function withRetry(send, sleep) {
|
|
41
|
+
let sawNetworkError = false;
|
|
42
|
+
let lastError;
|
|
43
|
+
for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
|
|
44
|
+
if (attempt > 0)
|
|
45
|
+
await sleep(RETRY_DELAYS_MS[attempt - 1]);
|
|
46
|
+
try {
|
|
47
|
+
const res = await send();
|
|
48
|
+
if (!retryable(res.status) || attempt === RETRY_DELAYS_MS.length) {
|
|
49
|
+
return { res, attempts: attempt + 1, sawNetworkError };
|
|
50
|
+
}
|
|
51
|
+
// Drain it so the connection is released before the next attempt.
|
|
52
|
+
await res.arrayBuffer().catch(() => undefined);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
sawNetworkError = true;
|
|
56
|
+
lastError = e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
60
|
+
}
|
|
61
|
+
function authHeaders(link, extra = {}) {
|
|
62
|
+
return { authorization: `Bearer ${link.token}`, accept: "application/json", ...extra };
|
|
63
|
+
}
|
|
64
|
+
/** Today's response to an ingest, read the same way for every route that returns it. */
|
|
65
|
+
async function ingestResult(res) {
|
|
66
|
+
const text = await res.text();
|
|
67
|
+
if (!res.ok)
|
|
68
|
+
return { error: `Trail refused the upload: ${res.status} ${text.slice(0, 200)}` };
|
|
69
|
+
try {
|
|
70
|
+
return { jobId: String(JSON.parse(text).job_id ?? "") };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return { jobId: "" };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The single POST to /repo — gzipped when the server takes it, plain otherwise.
|
|
78
|
+
* Not retried: a repeated POST of a whole ingest is not idempotent, and a 409
|
|
79
|
+
* for "already running" on the retry would read as a refusal.
|
|
80
|
+
*/
|
|
81
|
+
export async function postDigestOnce(link, digest, opts = {}) {
|
|
82
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
83
|
+
const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/repo${opts.query ?? ""}`;
|
|
84
|
+
const json = JSON.stringify(digest);
|
|
85
|
+
const body = opts.gzip ? await gzipAsync(Buffer.from(json, "utf8")) : json;
|
|
86
|
+
const headers = authHeaders(link, { "content-type": "application/json" });
|
|
87
|
+
if (opts.gzip)
|
|
88
|
+
headers["content-encoding"] = "gzip";
|
|
89
|
+
return fetchImpl(url, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers,
|
|
92
|
+
body: body,
|
|
93
|
+
...(opts.timeoutMs ? { signal: AbortSignal.timeout(opts.timeoutMs) } : {}),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Upload the full digest by whichever route the server advertises.
|
|
98
|
+
*
|
|
99
|
+
* Chunked when `caps.chunked`; if the upload session cannot even be opened
|
|
100
|
+
* (a 404 from a server that advertised the flag but not the route), it falls
|
|
101
|
+
* back to the single POST rather than failing a push that would otherwise work.
|
|
102
|
+
*/
|
|
103
|
+
export async function uploadDigest(link, digest, caps, opts = {}) {
|
|
104
|
+
if (caps.chunked) {
|
|
105
|
+
const chunked = await uploadChunked(link, digest, opts);
|
|
106
|
+
if (!("fallback" in chunked))
|
|
107
|
+
return chunked;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
return await ingestResult(await postDigestOnce(link, digest, { gzip: caps.gzip, fetchImpl: opts.fetchImpl }));
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
return { error: `could not reach Trail: ${e instanceof Error ? e.message : e}` };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The chunked route:
|
|
118
|
+
*
|
|
119
|
+
* POST /repo/uploads {stage, auto_approve, chunks, bytes} → {upload_id}
|
|
120
|
+
* PUT /repo/uploads/:id/chunks/:index raw gzip slice, idempotent → 204
|
|
121
|
+
* POST /repo/uploads/:id/complete → today's /repo response
|
|
122
|
+
*
|
|
123
|
+
* `{fallback: true}` means the session could not be opened and nothing was
|
|
124
|
+
* sent, so the caller may use the single POST instead.
|
|
125
|
+
*/
|
|
126
|
+
export async function uploadChunked(link, digest, opts = {}) {
|
|
127
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
128
|
+
const sleep = opts.sleep ?? realSleep;
|
|
129
|
+
const chunkBytes = opts.chunkBytes ?? CHUNK_BYTES;
|
|
130
|
+
const base = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/repo/uploads`;
|
|
131
|
+
const gz = await gzipDigest(digest);
|
|
132
|
+
const chunks = Math.max(1, Math.ceil(gz.length / chunkBytes));
|
|
133
|
+
let uploadId = "";
|
|
134
|
+
try {
|
|
135
|
+
const { res } = await withRetry(() => fetchImpl(base, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: authHeaders(link, { "content-type": "application/json" }),
|
|
138
|
+
body: JSON.stringify({ stage: "full", auto_approve: digest.auto_approve, chunks, bytes: gz.length }),
|
|
139
|
+
}), sleep);
|
|
140
|
+
const text = await res.text();
|
|
141
|
+
if (res.status === 404 || res.status === 405)
|
|
142
|
+
return { fallback: true };
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
// 409 is the one refusal worth passing on as-is: an ingest is already
|
|
145
|
+
// running, and a single POST would be told the same.
|
|
146
|
+
if (res.status === 409)
|
|
147
|
+
return { error: `Trail refused the upload: ${res.status} ${text.slice(0, 200)}` };
|
|
148
|
+
return { fallback: true };
|
|
149
|
+
}
|
|
150
|
+
uploadId = String(JSON.parse(text).upload_id ?? "");
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return { fallback: true };
|
|
154
|
+
}
|
|
155
|
+
if (!uploadId)
|
|
156
|
+
return { fallback: true };
|
|
157
|
+
// Chunks, four at a time, each retried on its own.
|
|
158
|
+
const concurrency = Math.max(1, opts.concurrency ?? CHUNK_CONCURRENCY);
|
|
159
|
+
let next = 0;
|
|
160
|
+
let failure = null;
|
|
161
|
+
const worker = async () => {
|
|
162
|
+
for (;;) {
|
|
163
|
+
if (failure)
|
|
164
|
+
return;
|
|
165
|
+
const i = next++;
|
|
166
|
+
if (i >= chunks)
|
|
167
|
+
return;
|
|
168
|
+
const slice = gz.subarray(i * chunkBytes, Math.min(gz.length, (i + 1) * chunkBytes));
|
|
169
|
+
try {
|
|
170
|
+
const { res } = await withRetry(() => fetchImpl(`${base}/${encodeURIComponent(uploadId)}/chunks/${i}`, {
|
|
171
|
+
method: "PUT",
|
|
172
|
+
headers: authHeaders(link, { "content-type": "application/octet-stream" }),
|
|
173
|
+
body: slice,
|
|
174
|
+
}), sleep);
|
|
175
|
+
if (!res.ok) {
|
|
176
|
+
const text = await res.text().catch(() => "");
|
|
177
|
+
failure = `Trail refused part ${i + 1} of ${chunks}: ${res.status} ${text.slice(0, 200)}`;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
await res.arrayBuffer().catch(() => undefined);
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
failure = `could not send part ${i + 1} of ${chunks} to Trail: ${e instanceof Error ? e.message : e}`;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks) }, worker));
|
|
189
|
+
if (failure)
|
|
190
|
+
return { error: failure };
|
|
191
|
+
try {
|
|
192
|
+
const { res, sawNetworkError } = await withRetry(() => fetchImpl(`${base}/${encodeURIComponent(uploadId)}/complete`, {
|
|
193
|
+
method: "POST",
|
|
194
|
+
headers: authHeaders(link, { "content-type": "application/json" }),
|
|
195
|
+
body: "{}",
|
|
196
|
+
}), sleep);
|
|
197
|
+
// A complete that was lost on the way back may have started the ingest, so
|
|
198
|
+
// its retry being told "already running" means it worked. The watcher that
|
|
199
|
+
// follows reads the build either way.
|
|
200
|
+
if (res.status === 409 && sawNetworkError) {
|
|
201
|
+
await res.arrayBuffer().catch(() => undefined);
|
|
202
|
+
return { jobId: "" };
|
|
203
|
+
}
|
|
204
|
+
return await ingestResult(res);
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
return { error: `could not reach Trail to finish the upload: ${e instanceof Error ? e.message : e}` };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/brain/upload.ts"],"names":[],"mappings":"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"}
|
package/dist/brain/watch.d.ts
CHANGED
|
@@ -69,6 +69,27 @@ export interface RepoState {
|
|
|
69
69
|
/** Rules the miner has handed over so far. Grows with every slice of the
|
|
70
70
|
* history that comes back, so it is a floor and never a total. */
|
|
71
71
|
foundSoFar?: number;
|
|
72
|
+
/** Suggested changes to the repo's context files, when Trail sends them. */
|
|
73
|
+
suggestions?: Suggestions;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The suggestions Trail has for this repository's context files so far.
|
|
77
|
+
*
|
|
78
|
+
* Counts, because that is all the terminal prints; the changes themselves are
|
|
79
|
+
* reviewed in Trail and written by `graft trail pull`.
|
|
80
|
+
*/
|
|
81
|
+
export interface Suggestions {
|
|
82
|
+
/** Changes suggested for the root CLAUDE.md. */
|
|
83
|
+
claudeMd: number;
|
|
84
|
+
/** Changes suggested for every other context file, all kinds together. */
|
|
85
|
+
contextFiles: number;
|
|
86
|
+
/** Per file, when Trail lists them — which is what lets the count be cut to
|
|
87
|
+
* the files the wired agents actually read. */
|
|
88
|
+
files?: Array<{
|
|
89
|
+
kind: string;
|
|
90
|
+
path: string;
|
|
91
|
+
changes: number;
|
|
92
|
+
}>;
|
|
72
93
|
}
|
|
73
94
|
export interface BuildView {
|
|
74
95
|
stages: Stage[];
|
|
@@ -91,6 +112,11 @@ export interface BuildView {
|
|
|
91
112
|
* to fix repository access for a problem that has nothing to do with it.
|
|
92
113
|
*/
|
|
93
114
|
export declare function stagesFrom(repo: RepoState | null): BuildView;
|
|
115
|
+
/**
|
|
116
|
+
* The repo state out of an expected-repo body — the polled GET and every SSE
|
|
117
|
+
* `progress` event carry the same shape. Null when there is no repo row.
|
|
118
|
+
*/
|
|
119
|
+
export declare function parseRepoBody(body: unknown): RepoState | null;
|
|
94
120
|
/**
|
|
95
121
|
* Read the repo row this brain is building.
|
|
96
122
|
*
|
|
@@ -99,8 +125,24 @@ export declare function stagesFrom(repo: RepoState | null): BuildView;
|
|
|
99
125
|
* nothing new rather than inventing a failure the build did not have.
|
|
100
126
|
*/
|
|
101
127
|
export declare function fetchRepoState(link: BrainLink, fetchImpl?: typeof fetch): Promise<RepoState | null>;
|
|
102
|
-
/**
|
|
103
|
-
export declare
|
|
128
|
+
/** What the spinner says while a stage is running. */
|
|
129
|
+
export declare const DOING_LABEL: Record<StageId, string>;
|
|
130
|
+
/** The first rules there are, from whichever count the row has. */
|
|
131
|
+
export declare function rulesSoFar(repo: RepoState | null): number;
|
|
132
|
+
/**
|
|
133
|
+
* One printable line per stage that has finished since it was last printed.
|
|
134
|
+
*
|
|
135
|
+
* Only for a log with no terminal to redraw — on a TTY the spinner says which
|
|
136
|
+
* stage is running and these lines are never printed. Filing is left out: the
|
|
137
|
+
* watcher hands the prompt back on the first rules, before filing finishes, and
|
|
138
|
+
* the closing line says how many there are.
|
|
139
|
+
*/
|
|
140
|
+
export declare function linesFor(view: BuildView, already: Set<string>, repo?: RepoState | null): string[];
|
|
141
|
+
/** The stage the build is in now, for the spinner. */
|
|
142
|
+
export declare function currentStage(view: BuildView): StageId;
|
|
143
|
+
/** The failure line: where it stopped, and that nothing was lost. The server's
|
|
144
|
+
* own words follow on the next line, in full. */
|
|
145
|
+
export declare function failureLines(view: BuildView): string[];
|
|
104
146
|
export interface WatchOptions {
|
|
105
147
|
/** How long to hold before giving up on a build that is still running. */
|
|
106
148
|
timeoutMs?: number;
|
|
@@ -109,6 +151,16 @@ export interface WatchOptions {
|
|
|
109
151
|
/** Where the lines go. Injected so the tests do not need a console. */
|
|
110
152
|
write?: (line: string) => void;
|
|
111
153
|
sleep?: (ms: number) => Promise<void>;
|
|
154
|
+
/** Print a line per finished stage. Off on a TTY, where a spinner says it. */
|
|
155
|
+
stageLines?: boolean;
|
|
156
|
+
/** Every state read, with its view — the spinner's feed. */
|
|
157
|
+
onState?: (view: BuildView, repo: RepoState) => void;
|
|
158
|
+
/** Follow `GET /events` instead of polling (the server advertised `events`). */
|
|
159
|
+
events?: boolean;
|
|
160
|
+
/** Silence on the stream for this long counts as a drop. Keep-alives come every 15 s. */
|
|
161
|
+
idleMs?: number;
|
|
162
|
+
/** Stream reconnects before falling back to polling. */
|
|
163
|
+
maxReconnects?: number;
|
|
112
164
|
}
|
|
113
165
|
/**
|
|
114
166
|
* How the watch ended.
|
|
@@ -120,12 +172,17 @@ export interface WatchOptions {
|
|
|
120
172
|
*/
|
|
121
173
|
export type WatchOutcome = "completed" | "building" | "failed" | "timed_out" | "unreachable";
|
|
122
174
|
/**
|
|
123
|
-
* Hold until the brain has rules,
|
|
175
|
+
* Hold until the brain has rules, reporting each stage as it settles.
|
|
124
176
|
*
|
|
125
177
|
* Returns on the first rules rather than on the finished job: mining is what
|
|
126
178
|
* fails and it is done by then, and filing the rest is minutes of work nobody
|
|
127
179
|
* needs to watch. The caller prints where to see the rest.
|
|
128
180
|
*
|
|
181
|
+
* Live when the server streams events: each change arrives as it happens
|
|
182
|
+
* instead of up to four seconds late. A stream that drops is reopened up to
|
|
183
|
+
* three times, and after that the watcher polls as it always did — so the
|
|
184
|
+
* worst a broken stream costs is the old behaviour.
|
|
185
|
+
*
|
|
129
186
|
* Fifteen minutes by default. That is far more than the mining now takes, and it
|
|
130
187
|
* stays generous on purpose: a watcher that gives up before the build does would
|
|
131
188
|
* report a healthy build as a timeout, which is the exact confusion this is
|
|
@@ -133,4 +190,20 @@ export type WatchOutcome = "completed" | "building" | "failed" | "timed_out" | "
|
|
|
133
190
|
* does.
|
|
134
191
|
*/
|
|
135
192
|
export declare function watchBuild(link: BrainLink, opts?: WatchOptions): Promise<WatchOutcome>;
|
|
193
|
+
/** One server-sent event. */
|
|
194
|
+
export interface SseEvent {
|
|
195
|
+
event: string;
|
|
196
|
+
data: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* An incremental text/event-stream parser: feed it chunks as they arrive,
|
|
200
|
+
* get back the events they completed. Comments (`: ping`) are dropped; a
|
|
201
|
+
* missing `event:` field means `message`, per the spec.
|
|
202
|
+
*/
|
|
203
|
+
export declare class SseParser {
|
|
204
|
+
private buf;
|
|
205
|
+
private event;
|
|
206
|
+
private data;
|
|
207
|
+
push(chunk: string): SseEvent[];
|
|
208
|
+
}
|
|
136
209
|
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/brain/watch.ts"],"names":[],"mappings":"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"}
|