heyquilt 0.3.138 → 0.3.140
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/RELEASES.md +6 -0
- package/bin/quilt.js +7 -143
- package/package.json +1 -1
- package/src/account.js +0 -29
- package/src/api/memory-store.js +0 -29
- package/src/api/routes/teams.js +0 -23
- package/src/api/server.js +5 -19
- package/src/api/supabase-store.js +0 -52
- package/src/branch-view.js +6 -131
- package/src/control.js +2 -33
- package/src/duties.js +1 -1
- package/src/github-commit.js +3 -315
- package/src/mcp.js +8 -157
- package/src/relay-ask.js +3 -66
- package/src/relay-cluster.js +3 -5
- package/src/relay-mcp.js +6 -418
- package/src/relay-prune.js +6 -145
- package/src/relay-turn-sync.js +1 -1
- package/src/server.js +33 -1331
- package/src/session.js +52 -3526
- package/src/status.js +2 -58
- package/src/turn-sync.js +15 -2
- package/src/ui/changes.js +5 -14
- package/src/ui/chat.js +0 -1
- package/src/ui/common.js +0 -2
- package/src/ui/git-rule.js +2 -30
- package/src/ui/home.js +3 -76
- package/src/ui/session.js +34 -528
- package/src/ui/tree.js +2 -8
- package/src/ui/workspaces.js +2 -122
- package/src/ui-server.js +1 -38
- package/src/api/routes/github.js +0 -380
- package/src/branches.js +0 -194
- package/src/clash.js +0 -128
- package/src/commit-request.js +0 -59
- package/src/commit.js +0 -213
- package/src/git-hook.js +0 -48
- package/src/gitignore.js +0 -49
- package/src/gitstate.js +0 -1069
- package/src/relay-ci.js +0 -111
- package/src/relay-clash.js +0 -214
- package/src/relay-commit.js +0 -923
- package/src/relay-filekey.js +0 -46
- package/src/relay-git.js +0 -274
- package/src/relay-githttp.js +0 -166
- package/src/relay-github.js +0 -109
- package/src/relay-outbound.js +0 -1295
- package/src/relay-repo-moves.js +0 -43
- package/src/relay-sync.js +0 -795
- package/src/relay-upstream.js +0 -889
- package/src/sync-words.js +0 -42
- package/src/upstream.js +0 -192
package/src/relay-prune.js
CHANGED
|
@@ -1,29 +1,13 @@
|
|
|
1
|
-
// Branches leave a session by themselves, so it never fills up with
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// told), or, for a merge made without a pull request, that all its commits are in its base
|
|
7
|
-
// branch (mergedByAncestry, looked at after the base moves).
|
|
8
|
-
// - Inactive: a branch nobody edited, committed to or was on for INACTIVE_MS leaves the session,
|
|
9
|
-
// whoever made it. It stays on GitHub.
|
|
10
|
-
// Never the default branch or main, nor a branch someone is on, holds a file on, or a hosted agent
|
|
11
|
-
// chose (while it was worked on this week). Work the session has that isn't on GitHub is never lost: a merged branch with edits after
|
|
12
|
-
// its last commit stays, and an inactive one is kept in the relay's archive (on disk, out of the
|
|
1
|
+
// Branches leave a session by themselves, so it never fills up with forgotten work (each branch is
|
|
2
|
+
// a whole copy of the project in the relay's memory and disk): a branch nobody edited or was on for
|
|
3
|
+
// INACTIVE_MS leaves the session, whoever made it. Never the default branch or main, nor a branch
|
|
4
|
+
// someone is on, holds a file on, or a hosted agent chose (while it was worked on this week), nor a
|
|
5
|
+
// square's live document. An inactive branch is kept in the relay's archive (on disk, out of the
|
|
13
6
|
// session) for ARCHIVE_KEEP_MS before it goes. A branch comes back whenever someone checks it out.
|
|
14
|
-
import {
|
|
15
|
-
import { appCredentials } from './relay-github.js'
|
|
16
|
-
import { githubWriter } from './github-commit.js'
|
|
17
|
-
import { parseRepo } from './relay-upstream.js'
|
|
18
|
-
import { RELAY_BY } from './branches.js'
|
|
7
|
+
import { RELAY_NAME as RELAY_BY } from './ui/chat.js'
|
|
19
8
|
|
|
20
9
|
export const INACTIVE_MS = 7 * 24 * 60 * 60 * 1000
|
|
21
10
|
export const ARCHIVE_KEEP_MS = 30 * 24 * 60 * 60 * 1000
|
|
22
|
-
export const ANCESTRY_EVERY_MS = 5 * 60 * 1000
|
|
23
|
-
// A branch's pull requests are looked up this often (pull requests merged before the relay heard of them).
|
|
24
|
-
export const PULLS_EVERY_MS = 60 * 60 * 1000
|
|
25
|
-
// A session edit this long after a branch's last commit is work that isn't in git.
|
|
26
|
-
const AFTER_COMMIT_MS = 60 * 1000
|
|
27
11
|
const TRUNKS = new Set(['main', 'master'])
|
|
28
12
|
|
|
29
13
|
/** Who is on which branch now: connected folders, claims, and hosted agents' choices of a branch worked on this week (chosenBranches). */
|
|
@@ -63,129 +47,6 @@ export function inactiveBranches (room, now = Date.now(), inactiveMs = INACTIVE_
|
|
|
63
47
|
.map(([k]) => k)
|
|
64
48
|
}
|
|
65
49
|
|
|
66
|
-
/**
|
|
67
|
-
* Whether everything the session has on branch `key` is on GitHub: it follows a GitHub branch at a
|
|
68
|
-
* known commit, nothing is waiting to go there, and nobody edited it after that commit (`committedAt`).
|
|
69
|
-
*/
|
|
70
|
-
export function savedOnGithub (room, key, committedAt = 0) {
|
|
71
|
-
const rec = upstreamsOf(room)[key]
|
|
72
|
-
if (!rec || !rec.repo || !rec.sha) return false
|
|
73
|
-
const pending = room.meta.sync && room.meta.sync[key] && room.meta.sync[key].pending
|
|
74
|
-
if (pending && Object.keys(pending).length) return false
|
|
75
|
-
const b = room.meta.branches[key]
|
|
76
|
-
return !(b && b.changed && committedAt && b.changed > committedAt + AFTER_COMMIT_MS)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Merges made without a pull request: for each session branch following GitHub, whether all its
|
|
81
|
-
* commits are in its base (the branch it started from), the default branch, or main. GitHub is asked
|
|
82
|
-
* only when one of the two commits moved since it was last asked. Marks them merged (the way a pull
|
|
83
|
-
* request's merge is recorded) and returns their keys.
|
|
84
|
-
*/
|
|
85
|
-
export async function mergedByAncestry (room, { reader, now = Date.now(), patchesIn = null } = {}) {
|
|
86
|
-
const ups = upstreamsOf(room)
|
|
87
|
-
const marked = []
|
|
88
|
-
for (const [key, b] of Object.entries(room.meta.branches)) {
|
|
89
|
-
const rec = ups[key]
|
|
90
|
-
if (!rec || !rec.sha || !rec.repo || (rec.merged && rec.merged.at) || key === room.defaultKey || TRUNKS.has(key)) continue
|
|
91
|
-
const bases = new Set([b && b.base, room.defaultKey, ...TRUNKS].filter((x) => x && x !== key && room.meta.branches[x]))
|
|
92
|
-
for (const base of bases) {
|
|
93
|
-
const brec = ups[base]
|
|
94
|
-
if (!brec || !brec.sha || brec.repo !== rec.repo || brec.sha === rec.sha && !b.changed) continue
|
|
95
|
-
const asked = `${brec.sha}:${rec.sha}`
|
|
96
|
-
rec.ancestry = rec.ancestry || {}
|
|
97
|
-
if (rec.ancestry[base] === asked) continue
|
|
98
|
-
const gh = await reader(rec.repo)
|
|
99
|
-
if (!gh) break
|
|
100
|
-
let cmp
|
|
101
|
-
try { cmp = await gh.compare(brec.sha, rec.sha) } catch { continue } // looked at again next time
|
|
102
|
-
rec.ancestry[base] = asked
|
|
103
|
-
// Diverged, but every commit of the branch is already in the base as the same change (cherry-picked, or
|
|
104
|
-
// rebased in without a pull request): `patchesIn(base, head)` asks the relay's git mirror (git cherry).
|
|
105
|
-
const samePatches = cmp && cmp.status === 'diverged' && patchesIn ? await patchesIn(rec.repo, brec.sha, rec.sha).catch(() => false) : false
|
|
106
|
-
if (!cmp || (cmp.status !== 'behind' && cmp.status !== 'identical' && !samePatches)) continue
|
|
107
|
-
// Edited in the session after its last commit: that work isn't in git, so it isn't merged.
|
|
108
|
-
if (b && b.changed) {
|
|
109
|
-
let at = 0
|
|
110
|
-
try { at = await gh.committedAt(rec.sha) } catch { continue }
|
|
111
|
-
if (b.changed > at + AFTER_COMMIT_MS) continue
|
|
112
|
-
}
|
|
113
|
-
// Every commit of the branch is in its base: merged, as far as git can tell. A branch never
|
|
114
|
-
// committed to (identical to where it started) has nothing of its own either way.
|
|
115
|
-
rec.merged = { at: now, into: brec.ref || base, number: null, sha: brec.sha, via: 'commits' }
|
|
116
|
-
marked.push(key)
|
|
117
|
-
break
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (marked.length && room.saveMeta) room.saveMeta()
|
|
121
|
-
return marked
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Pull requests merged before the relay recorded merges, or whose webhook never came: for each
|
|
126
|
-
* session branch with no merge recorded, asks the session's repository (at most every
|
|
127
|
-
* PULLS_EVERY_MS a branch) for a merged pull request from it into a branch the session has, made
|
|
128
|
-
* after the branch came into the session (an older one is another branch of the same name). Marks
|
|
129
|
-
* them merged as the webhook would and returns their keys.
|
|
130
|
-
*/
|
|
131
|
-
export async function mergedByPull (room, { reader, now = Date.now(), lookedAt = new Map(), log = () => {} } = {}) {
|
|
132
|
-
const ups = upstreamsOf(room)
|
|
133
|
-
const repo = (ups[room.defaultKey] && ups[room.defaultKey].repo) || (ups.main && ups.main.repo)
|
|
134
|
-
if (!repo) return []
|
|
135
|
-
const marked = []
|
|
136
|
-
let gh
|
|
137
|
-
for (const [key, b] of Object.entries(room.meta.branches)) {
|
|
138
|
-
const rec = ups[key]
|
|
139
|
-
if ((rec && rec.merged && rec.merged.at) || key === room.defaultKey || TRUNKS.has(key) || key.startsWith('@')) continue
|
|
140
|
-
if (rec && rec.repo && rec.repo !== repo) continue
|
|
141
|
-
if (now - (lookedAt.get(key) || 0) < PULLS_EVERY_MS) continue
|
|
142
|
-
if (gh === undefined) gh = await reader(repo)
|
|
143
|
-
if (!gh) return marked
|
|
144
|
-
const ref = (rec && rec.ref) || key
|
|
145
|
-
let pull
|
|
146
|
-
try { pull = await gh.mergedPull(ref) } catch (err) { log(`[${room.name}] could not look up ${ref}'s pull requests: ${err.message}`); return marked } // looked at again next time
|
|
147
|
-
lookedAt.set(key, now)
|
|
148
|
-
if (!pull || !pull.base || !(pull.mergedAt > 0) || pull.base === ref) continue
|
|
149
|
-
const into = room.resolveKey ? room.resolveKey(pull.base) : pull.base
|
|
150
|
-
if (!room.meta.branches[into] || into === key) continue
|
|
151
|
-
if (b && b.at && b.at > pull.mergedAt) continue
|
|
152
|
-
const merged = { at: pull.mergedAt, into: pull.base, number: pull.number, sha: pull.sha, via: 'pull' }
|
|
153
|
-
if (rec) rec.merged = merged
|
|
154
|
-
else ups[key] = { merged: { ...merged, repo, ref } } // not followed on GitHub: only the merge, and where its branch was
|
|
155
|
-
marked.push(key)
|
|
156
|
-
}
|
|
157
|
-
if (marked.length && room.saveMeta) room.saveMeta()
|
|
158
|
-
return marked
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Whether every commit of `head` not in `base` is in `base` as the same change (`git cherry` in mirror `dir`; both commits there). */
|
|
162
|
-
export async function samePatches (dir, base, head) {
|
|
163
|
-
const { runGit } = await import('./relay-git.js')
|
|
164
|
-
const out = await runGit(dir, ['cherry', base, head])
|
|
165
|
-
if (!out.ok) return false
|
|
166
|
-
const lines = out.out.split('\n').filter(Boolean)
|
|
167
|
-
return lines.length > 0 && lines.every((l) => l.startsWith('-'))
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/** Deletes merged branch `ref` on GitHub, unless it is a trunk or still has an open pull request. Never throws. */
|
|
171
|
-
export async function deleteOnGithub (room, repoStr, ref, { fetch, log = () => {} } = {}) {
|
|
172
|
-
if (!ref || TRUNKS.has(ref)) return false
|
|
173
|
-
const repo = parseRepo(`https://${repoStr}`)
|
|
174
|
-
if (!repo || !repo.github) return false
|
|
175
|
-
try {
|
|
176
|
-
const { token } = await appCredentials(room, `${repo.owner}/${repo.name}`, { access: 'write', fresh: false })
|
|
177
|
-
if (!token) return false
|
|
178
|
-
const res = await fetch(`https://api.github.com/repos/${repo.owner}/${repo.name}/pulls?state=open&head=${encodeURIComponent(`${repo.owner}:${ref}`)}`, { headers: { authorization: `Bearer ${token}`, accept: 'application/vnd.github+json', 'user-agent': 'quilt-relay', 'x-github-api-version': '2022-11-28' } })
|
|
179
|
-
const open = res.ok ? await res.json() : null
|
|
180
|
-
if (!Array.isArray(open) || open.length) return false // can't tell, or a pull request is still open: leave it
|
|
181
|
-
await githubWriter({ fetch, token, owner: repo.owner, name: repo.name }).deleteBranch(ref)
|
|
182
|
-
return true
|
|
183
|
-
} catch (err) {
|
|
184
|
-
log(`[${room.name}] could not delete ${ref} on GitHub: ${err.message}`)
|
|
185
|
-
return false
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
50
|
/** One chat line for what left the session, so nobody is surprised. */
|
|
190
51
|
export function pruneNotice ({ merged = [], inactive = [], archived = [] }) {
|
|
191
52
|
const parts = []
|
package/src/relay-turn-sync.js
CHANGED
|
@@ -24,7 +24,7 @@ import { applyTextDiff } from './textdiff.js'
|
|
|
24
24
|
import { isSafeRelPath, LARGE_FILE_BYTES } from './pathrules.js'
|
|
25
25
|
import { openMerge, updateMerge, readMerges, cleanName, MAX_RECORD_TEXT } from './merges.js'
|
|
26
26
|
import { unionMatcher } from './merge3.js'
|
|
27
|
-
import { RELAY_BY, isRelay } from './
|
|
27
|
+
import { RELAY_NAME as RELAY_BY, isRelay } from './ui/chat.js'
|
|
28
28
|
import { decide, planSync, nextCheckpoint, mergeTexts, behind, sessionEntry, SKIP } from './turn-sync-plan.js'
|
|
29
29
|
|
|
30
30
|
export const SLOT = /^[A-Za-z0-9_-]{8,64}$/
|