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.
Files changed (52) hide show
  1. package/RELEASES.md +6 -0
  2. package/bin/quilt.js +7 -143
  3. package/package.json +1 -1
  4. package/src/account.js +0 -29
  5. package/src/api/memory-store.js +0 -29
  6. package/src/api/routes/teams.js +0 -23
  7. package/src/api/server.js +5 -19
  8. package/src/api/supabase-store.js +0 -52
  9. package/src/branch-view.js +6 -131
  10. package/src/control.js +2 -33
  11. package/src/duties.js +1 -1
  12. package/src/github-commit.js +3 -315
  13. package/src/mcp.js +8 -157
  14. package/src/relay-ask.js +3 -66
  15. package/src/relay-cluster.js +3 -5
  16. package/src/relay-mcp.js +6 -418
  17. package/src/relay-prune.js +6 -145
  18. package/src/relay-turn-sync.js +1 -1
  19. package/src/server.js +33 -1331
  20. package/src/session.js +52 -3526
  21. package/src/status.js +2 -58
  22. package/src/turn-sync.js +15 -2
  23. package/src/ui/changes.js +5 -14
  24. package/src/ui/chat.js +0 -1
  25. package/src/ui/common.js +0 -2
  26. package/src/ui/git-rule.js +2 -30
  27. package/src/ui/home.js +3 -76
  28. package/src/ui/session.js +34 -528
  29. package/src/ui/tree.js +2 -8
  30. package/src/ui/workspaces.js +2 -122
  31. package/src/ui-server.js +1 -38
  32. package/src/api/routes/github.js +0 -380
  33. package/src/branches.js +0 -194
  34. package/src/clash.js +0 -128
  35. package/src/commit-request.js +0 -59
  36. package/src/commit.js +0 -213
  37. package/src/git-hook.js +0 -48
  38. package/src/gitignore.js +0 -49
  39. package/src/gitstate.js +0 -1069
  40. package/src/relay-ci.js +0 -111
  41. package/src/relay-clash.js +0 -214
  42. package/src/relay-commit.js +0 -923
  43. package/src/relay-filekey.js +0 -46
  44. package/src/relay-git.js +0 -274
  45. package/src/relay-githttp.js +0 -166
  46. package/src/relay-github.js +0 -109
  47. package/src/relay-outbound.js +0 -1295
  48. package/src/relay-repo-moves.js +0 -43
  49. package/src/relay-sync.js +0 -795
  50. package/src/relay-upstream.js +0 -889
  51. package/src/sync-words.js +0 -42
  52. package/src/upstream.js +0 -192
@@ -1,29 +1,13 @@
1
- // Branches leave a session by themselves, so it never fills up with finished or forgotten work
2
- // (each branch is a whole copy of the project in the relay's memory and disk). Two rules:
3
- // - Merged: a branch whose work is in another branch the session tracks leaves the session, and
4
- // its branch on GitHub is deleted. Merged means its pull request merged (relay-ask.js
5
- // mergedBranches, from GitHub's webhook, or mergedByPull asking GitHub for one the webhook never
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 { upstreamsOf, githubReader } from './relay-upstream.js'
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 = []
@@ -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 './branches.js'
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}$/