@hcrosse/opencode-pr-tracker 0.2.0 → 0.4.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 +68 -53
- package/dist/server.js +1242 -1896
- package/dist/server.js.map +30 -18
- package/dist/tui.js +1122 -2917
- package/dist/tui.js.map +20 -21
- package/package.json +35 -26
- package/server.js +1 -0
- package/tui.js +1 -0
package/dist/tui.js
CHANGED
|
@@ -1,3012 +1,1217 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function __accessProp(key) {
|
|
8
|
-
return this[key];
|
|
9
|
-
}
|
|
10
|
-
var __toESMCache_node;
|
|
11
|
-
var __toESMCache_esm;
|
|
12
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
-
var canCache = mod != null && typeof mod === "object";
|
|
14
|
-
if (canCache) {
|
|
15
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
-
var cached = cache.get(mod);
|
|
17
|
-
if (cached)
|
|
18
|
-
return cached;
|
|
19
|
-
}
|
|
20
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
-
for (let key of __getOwnPropNames(mod))
|
|
23
|
-
if (!__hasOwnProp.call(to, key))
|
|
24
|
-
__defProp(to, key, {
|
|
25
|
-
get: __accessProp.bind(mod, key),
|
|
26
|
-
enumerable: true
|
|
27
|
-
});
|
|
28
|
-
if (canCache)
|
|
29
|
-
cache.set(mod, to);
|
|
30
|
-
return to;
|
|
31
|
-
};
|
|
32
|
-
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
-
var __require = import.meta.require;
|
|
34
|
-
|
|
35
|
-
// node_modules/.bun/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js
|
|
36
|
-
var require_polyfills = __commonJS((exports, module) => {
|
|
37
|
-
var constants = __require("constants");
|
|
38
|
-
var origCwd = process.cwd;
|
|
39
|
-
var cwd = null;
|
|
40
|
-
var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
41
|
-
process.cwd = function() {
|
|
42
|
-
if (!cwd)
|
|
43
|
-
cwd = origCwd.call(process);
|
|
44
|
-
return cwd;
|
|
45
|
-
};
|
|
46
|
-
try {
|
|
47
|
-
process.cwd();
|
|
48
|
-
} catch (er) {}
|
|
49
|
-
if (typeof process.chdir === "function") {
|
|
50
|
-
chdir = process.chdir;
|
|
51
|
-
process.chdir = function(d) {
|
|
52
|
-
cwd = null;
|
|
53
|
-
chdir.call(process, d);
|
|
54
|
-
};
|
|
55
|
-
if (Object.setPrototypeOf)
|
|
56
|
-
Object.setPrototypeOf(process.chdir, chdir);
|
|
57
|
-
}
|
|
58
|
-
var chdir;
|
|
59
|
-
module.exports = patch;
|
|
60
|
-
function patch(fs) {
|
|
61
|
-
if (constants.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
62
|
-
patchLchmod(fs);
|
|
63
|
-
}
|
|
64
|
-
if (!fs.lutimes) {
|
|
65
|
-
patchLutimes(fs);
|
|
66
|
-
}
|
|
67
|
-
fs.chown = chownFix(fs.chown);
|
|
68
|
-
fs.fchown = chownFix(fs.fchown);
|
|
69
|
-
fs.lchown = chownFix(fs.lchown);
|
|
70
|
-
fs.chmod = chmodFix(fs.chmod);
|
|
71
|
-
fs.fchmod = chmodFix(fs.fchmod);
|
|
72
|
-
fs.lchmod = chmodFix(fs.lchmod);
|
|
73
|
-
fs.chownSync = chownFixSync(fs.chownSync);
|
|
74
|
-
fs.fchownSync = chownFixSync(fs.fchownSync);
|
|
75
|
-
fs.lchownSync = chownFixSync(fs.lchownSync);
|
|
76
|
-
fs.chmodSync = chmodFixSync(fs.chmodSync);
|
|
77
|
-
fs.fchmodSync = chmodFixSync(fs.fchmodSync);
|
|
78
|
-
fs.lchmodSync = chmodFixSync(fs.lchmodSync);
|
|
79
|
-
fs.stat = statFix(fs.stat);
|
|
80
|
-
fs.fstat = statFix(fs.fstat);
|
|
81
|
-
fs.lstat = statFix(fs.lstat);
|
|
82
|
-
fs.statSync = statFixSync(fs.statSync);
|
|
83
|
-
fs.fstatSync = statFixSync(fs.fstatSync);
|
|
84
|
-
fs.lstatSync = statFixSync(fs.lstatSync);
|
|
85
|
-
if (fs.chmod && !fs.lchmod) {
|
|
86
|
-
fs.lchmod = function(path, mode, cb) {
|
|
87
|
-
if (cb)
|
|
88
|
-
process.nextTick(cb);
|
|
89
|
-
};
|
|
90
|
-
fs.lchmodSync = function() {};
|
|
91
|
-
}
|
|
92
|
-
if (fs.chown && !fs.lchown) {
|
|
93
|
-
fs.lchown = function(path, uid, gid, cb) {
|
|
94
|
-
if (cb)
|
|
95
|
-
process.nextTick(cb);
|
|
96
|
-
};
|
|
97
|
-
fs.lchownSync = function() {};
|
|
98
|
-
}
|
|
99
|
-
if (platform === "win32") {
|
|
100
|
-
fs.rename = typeof fs.rename !== "function" ? fs.rename : function(fs$rename) {
|
|
101
|
-
function rename(from, to, cb) {
|
|
102
|
-
var start = Date.now();
|
|
103
|
-
var backoff = 0;
|
|
104
|
-
fs$rename(from, to, function CB(er) {
|
|
105
|
-
if (er && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY") && Date.now() - start < 60000) {
|
|
106
|
-
setTimeout(function() {
|
|
107
|
-
fs.stat(to, function(stater, st) {
|
|
108
|
-
if (stater && stater.code === "ENOENT")
|
|
109
|
-
fs$rename(from, to, CB);
|
|
110
|
-
else
|
|
111
|
-
cb(er);
|
|
112
|
-
});
|
|
113
|
-
}, backoff);
|
|
114
|
-
if (backoff < 100)
|
|
115
|
-
backoff += 10;
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (cb)
|
|
119
|
-
cb(er);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
if (Object.setPrototypeOf)
|
|
123
|
-
Object.setPrototypeOf(rename, fs$rename);
|
|
124
|
-
return rename;
|
|
125
|
-
}(fs.rename);
|
|
126
|
-
}
|
|
127
|
-
fs.read = typeof fs.read !== "function" ? fs.read : function(fs$read) {
|
|
128
|
-
function read(fd, buffer, offset, length, position, callback_) {
|
|
129
|
-
var callback;
|
|
130
|
-
if (callback_ && typeof callback_ === "function") {
|
|
131
|
-
var eagCounter = 0;
|
|
132
|
-
callback = function(er, _, __) {
|
|
133
|
-
if (er && er.code === "EAGAIN" && eagCounter < 10) {
|
|
134
|
-
eagCounter++;
|
|
135
|
-
return fs$read.call(fs, fd, buffer, offset, length, position, callback);
|
|
136
|
-
}
|
|
137
|
-
callback_.apply(this, arguments);
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
return fs$read.call(fs, fd, buffer, offset, length, position, callback);
|
|
141
|
-
}
|
|
142
|
-
if (Object.setPrototypeOf)
|
|
143
|
-
Object.setPrototypeOf(read, fs$read);
|
|
144
|
-
return read;
|
|
145
|
-
}(fs.read);
|
|
146
|
-
fs.readSync = typeof fs.readSync !== "function" ? fs.readSync : function(fs$readSync) {
|
|
147
|
-
return function(fd, buffer, offset, length, position) {
|
|
148
|
-
var eagCounter = 0;
|
|
149
|
-
while (true) {
|
|
150
|
-
try {
|
|
151
|
-
return fs$readSync.call(fs, fd, buffer, offset, length, position);
|
|
152
|
-
} catch (er) {
|
|
153
|
-
if (er.code === "EAGAIN" && eagCounter < 10) {
|
|
154
|
-
eagCounter++;
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
throw er;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
}(fs.readSync);
|
|
162
|
-
function patchLchmod(fs2) {
|
|
163
|
-
fs2.lchmod = function(path, mode, callback) {
|
|
164
|
-
fs2.open(path, constants.O_WRONLY | constants.O_SYMLINK, mode, function(err, fd) {
|
|
165
|
-
if (err) {
|
|
166
|
-
if (callback)
|
|
167
|
-
callback(err);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
fs2.fchmod(fd, mode, function(err2) {
|
|
171
|
-
fs2.close(fd, function(err22) {
|
|
172
|
-
if (callback)
|
|
173
|
-
callback(err2 || err22);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
fs2.lchmodSync = function(path, mode) {
|
|
179
|
-
var fd = fs2.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode);
|
|
180
|
-
var threw = true;
|
|
181
|
-
var ret;
|
|
182
|
-
try {
|
|
183
|
-
ret = fs2.fchmodSync(fd, mode);
|
|
184
|
-
threw = false;
|
|
185
|
-
} finally {
|
|
186
|
-
if (threw) {
|
|
187
|
-
try {
|
|
188
|
-
fs2.closeSync(fd);
|
|
189
|
-
} catch (er) {}
|
|
190
|
-
} else {
|
|
191
|
-
fs2.closeSync(fd);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return ret;
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
function patchLutimes(fs2) {
|
|
198
|
-
if (constants.hasOwnProperty("O_SYMLINK") && fs2.futimes) {
|
|
199
|
-
fs2.lutimes = function(path, at, mt, cb) {
|
|
200
|
-
fs2.open(path, constants.O_SYMLINK, function(er, fd) {
|
|
201
|
-
if (er) {
|
|
202
|
-
if (cb)
|
|
203
|
-
cb(er);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
fs2.futimes(fd, at, mt, function(er2) {
|
|
207
|
-
fs2.close(fd, function(er22) {
|
|
208
|
-
if (cb)
|
|
209
|
-
cb(er2 || er22);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
};
|
|
214
|
-
fs2.lutimesSync = function(path, at, mt) {
|
|
215
|
-
var fd = fs2.openSync(path, constants.O_SYMLINK);
|
|
216
|
-
var ret;
|
|
217
|
-
var threw = true;
|
|
218
|
-
try {
|
|
219
|
-
ret = fs2.futimesSync(fd, at, mt);
|
|
220
|
-
threw = false;
|
|
221
|
-
} finally {
|
|
222
|
-
if (threw) {
|
|
223
|
-
try {
|
|
224
|
-
fs2.closeSync(fd);
|
|
225
|
-
} catch (er) {}
|
|
226
|
-
} else {
|
|
227
|
-
fs2.closeSync(fd);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return ret;
|
|
231
|
-
};
|
|
232
|
-
} else if (fs2.futimes) {
|
|
233
|
-
fs2.lutimes = function(_a, _b, _c, cb) {
|
|
234
|
-
if (cb)
|
|
235
|
-
process.nextTick(cb);
|
|
236
|
-
};
|
|
237
|
-
fs2.lutimesSync = function() {};
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function chmodFix(orig) {
|
|
241
|
-
if (!orig)
|
|
242
|
-
return orig;
|
|
243
|
-
return function(target, mode, cb) {
|
|
244
|
-
return orig.call(fs, target, mode, function(er) {
|
|
245
|
-
if (chownErOk(er))
|
|
246
|
-
er = null;
|
|
247
|
-
if (cb)
|
|
248
|
-
cb.apply(this, arguments);
|
|
249
|
-
});
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
function chmodFixSync(orig) {
|
|
253
|
-
if (!orig)
|
|
254
|
-
return orig;
|
|
255
|
-
return function(target, mode) {
|
|
256
|
-
try {
|
|
257
|
-
return orig.call(fs, target, mode);
|
|
258
|
-
} catch (er) {
|
|
259
|
-
if (!chownErOk(er))
|
|
260
|
-
throw er;
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
function chownFix(orig) {
|
|
265
|
-
if (!orig)
|
|
266
|
-
return orig;
|
|
267
|
-
return function(target, uid, gid, cb) {
|
|
268
|
-
return orig.call(fs, target, uid, gid, function(er) {
|
|
269
|
-
if (chownErOk(er))
|
|
270
|
-
er = null;
|
|
271
|
-
if (cb)
|
|
272
|
-
cb.apply(this, arguments);
|
|
273
|
-
});
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
function chownFixSync(orig) {
|
|
277
|
-
if (!orig)
|
|
278
|
-
return orig;
|
|
279
|
-
return function(target, uid, gid) {
|
|
280
|
-
try {
|
|
281
|
-
return orig.call(fs, target, uid, gid);
|
|
282
|
-
} catch (er) {
|
|
283
|
-
if (!chownErOk(er))
|
|
284
|
-
throw er;
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
function statFix(orig) {
|
|
289
|
-
if (!orig)
|
|
290
|
-
return orig;
|
|
291
|
-
return function(target, options, cb) {
|
|
292
|
-
if (typeof options === "function") {
|
|
293
|
-
cb = options;
|
|
294
|
-
options = null;
|
|
295
|
-
}
|
|
296
|
-
function callback(er, stats) {
|
|
297
|
-
if (stats) {
|
|
298
|
-
if (stats.uid < 0)
|
|
299
|
-
stats.uid += 4294967296;
|
|
300
|
-
if (stats.gid < 0)
|
|
301
|
-
stats.gid += 4294967296;
|
|
302
|
-
}
|
|
303
|
-
if (cb)
|
|
304
|
-
cb.apply(this, arguments);
|
|
305
|
-
}
|
|
306
|
-
return options ? orig.call(fs, target, options, callback) : orig.call(fs, target, callback);
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
function statFixSync(orig) {
|
|
310
|
-
if (!orig)
|
|
311
|
-
return orig;
|
|
312
|
-
return function(target, options) {
|
|
313
|
-
var stats = options ? orig.call(fs, target, options) : orig.call(fs, target);
|
|
314
|
-
if (stats) {
|
|
315
|
-
if (stats.uid < 0)
|
|
316
|
-
stats.uid += 4294967296;
|
|
317
|
-
if (stats.gid < 0)
|
|
318
|
-
stats.gid += 4294967296;
|
|
319
|
-
}
|
|
320
|
-
return stats;
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
function chownErOk(er) {
|
|
324
|
-
if (!er)
|
|
325
|
-
return true;
|
|
326
|
-
if (er.code === "ENOSYS")
|
|
327
|
-
return true;
|
|
328
|
-
var nonroot = !process.getuid || process.getuid() !== 0;
|
|
329
|
-
if (nonroot) {
|
|
330
|
-
if (er.code === "EINVAL" || er.code === "EPERM")
|
|
331
|
-
return true;
|
|
332
|
-
}
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// node_modules/.bun/graceful-fs@4.2.11/node_modules/graceful-fs/legacy-streams.js
|
|
339
|
-
var require_legacy_streams = __commonJS((exports, module) => {
|
|
340
|
-
var Stream = __require("stream").Stream;
|
|
341
|
-
module.exports = legacy;
|
|
342
|
-
function legacy(fs) {
|
|
343
|
-
return {
|
|
344
|
-
ReadStream,
|
|
345
|
-
WriteStream
|
|
346
|
-
};
|
|
347
|
-
function ReadStream(path, options) {
|
|
348
|
-
if (!(this instanceof ReadStream))
|
|
349
|
-
return new ReadStream(path, options);
|
|
350
|
-
Stream.call(this);
|
|
351
|
-
var self = this;
|
|
352
|
-
this.path = path;
|
|
353
|
-
this.fd = null;
|
|
354
|
-
this.readable = true;
|
|
355
|
-
this.paused = false;
|
|
356
|
-
this.flags = "r";
|
|
357
|
-
this.mode = 438;
|
|
358
|
-
this.bufferSize = 64 * 1024;
|
|
359
|
-
options = options || {};
|
|
360
|
-
var keys = Object.keys(options);
|
|
361
|
-
for (var index = 0, length = keys.length;index < length; index++) {
|
|
362
|
-
var key = keys[index];
|
|
363
|
-
this[key] = options[key];
|
|
364
|
-
}
|
|
365
|
-
if (this.encoding)
|
|
366
|
-
this.setEncoding(this.encoding);
|
|
367
|
-
if (this.start !== undefined) {
|
|
368
|
-
if (typeof this.start !== "number") {
|
|
369
|
-
throw TypeError("start must be a Number");
|
|
370
|
-
}
|
|
371
|
-
if (this.end === undefined) {
|
|
372
|
-
this.end = Infinity;
|
|
373
|
-
} else if (typeof this.end !== "number") {
|
|
374
|
-
throw TypeError("end must be a Number");
|
|
375
|
-
}
|
|
376
|
-
if (this.start > this.end) {
|
|
377
|
-
throw new Error("start must be <= end");
|
|
378
|
-
}
|
|
379
|
-
this.pos = this.start;
|
|
380
|
-
}
|
|
381
|
-
if (this.fd !== null) {
|
|
382
|
-
process.nextTick(function() {
|
|
383
|
-
self._read();
|
|
384
|
-
});
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
fs.open(this.path, this.flags, this.mode, function(err, fd) {
|
|
388
|
-
if (err) {
|
|
389
|
-
self.emit("error", err);
|
|
390
|
-
self.readable = false;
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
self.fd = fd;
|
|
394
|
-
self.emit("open", fd);
|
|
395
|
-
self._read();
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
function WriteStream(path, options) {
|
|
399
|
-
if (!(this instanceof WriteStream))
|
|
400
|
-
return new WriteStream(path, options);
|
|
401
|
-
Stream.call(this);
|
|
402
|
-
this.path = path;
|
|
403
|
-
this.fd = null;
|
|
404
|
-
this.writable = true;
|
|
405
|
-
this.flags = "w";
|
|
406
|
-
this.encoding = "binary";
|
|
407
|
-
this.mode = 438;
|
|
408
|
-
this.bytesWritten = 0;
|
|
409
|
-
options = options || {};
|
|
410
|
-
var keys = Object.keys(options);
|
|
411
|
-
for (var index = 0, length = keys.length;index < length; index++) {
|
|
412
|
-
var key = keys[index];
|
|
413
|
-
this[key] = options[key];
|
|
414
|
-
}
|
|
415
|
-
if (this.start !== undefined) {
|
|
416
|
-
if (typeof this.start !== "number") {
|
|
417
|
-
throw TypeError("start must be a Number");
|
|
418
|
-
}
|
|
419
|
-
if (this.start < 0) {
|
|
420
|
-
throw new Error("start must be >= zero");
|
|
421
|
-
}
|
|
422
|
-
this.pos = this.start;
|
|
423
|
-
}
|
|
424
|
-
this.busy = false;
|
|
425
|
-
this._queue = [];
|
|
426
|
-
if (this.fd === null) {
|
|
427
|
-
this._open = fs.open;
|
|
428
|
-
this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
|
|
429
|
-
this.flush();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
// node_modules/.bun/graceful-fs@4.2.11/node_modules/graceful-fs/clone.js
|
|
436
|
-
var require_clone = __commonJS((exports, module) => {
|
|
437
|
-
module.exports = clone;
|
|
438
|
-
var getPrototypeOf = Object.getPrototypeOf || function(obj) {
|
|
439
|
-
return obj.__proto__;
|
|
440
|
-
};
|
|
441
|
-
function clone(obj) {
|
|
442
|
-
if (obj === null || typeof obj !== "object")
|
|
443
|
-
return obj;
|
|
444
|
-
if (obj instanceof Object)
|
|
445
|
-
var copy = { __proto__: getPrototypeOf(obj) };
|
|
446
|
-
else
|
|
447
|
-
var copy = Object.create(null);
|
|
448
|
-
Object.getOwnPropertyNames(obj).forEach(function(key) {
|
|
449
|
-
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key));
|
|
450
|
-
});
|
|
451
|
-
return copy;
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
// node_modules/.bun/graceful-fs@4.2.11/node_modules/graceful-fs/graceful-fs.js
|
|
456
|
-
var require_graceful_fs = __commonJS((exports, module) => {
|
|
457
|
-
var fs = __require("fs");
|
|
458
|
-
var polyfills = require_polyfills();
|
|
459
|
-
var legacy = require_legacy_streams();
|
|
460
|
-
var clone = require_clone();
|
|
461
|
-
var util = __require("util");
|
|
462
|
-
var gracefulQueue;
|
|
463
|
-
var previousSymbol;
|
|
464
|
-
if (typeof Symbol === "function" && typeof Symbol.for === "function") {
|
|
465
|
-
gracefulQueue = Symbol.for("graceful-fs.queue");
|
|
466
|
-
previousSymbol = Symbol.for("graceful-fs.previous");
|
|
467
|
-
} else {
|
|
468
|
-
gracefulQueue = "___graceful-fs.queue";
|
|
469
|
-
previousSymbol = "___graceful-fs.previous";
|
|
470
|
-
}
|
|
471
|
-
function noop() {}
|
|
472
|
-
function publishQueue(context, queue2) {
|
|
473
|
-
Object.defineProperty(context, gracefulQueue, {
|
|
474
|
-
get: function() {
|
|
475
|
-
return queue2;
|
|
476
|
-
}
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
var debug = noop;
|
|
480
|
-
if (util.debuglog)
|
|
481
|
-
debug = util.debuglog("gfs4");
|
|
482
|
-
else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ""))
|
|
483
|
-
debug = function() {
|
|
484
|
-
var m = util.format.apply(util, arguments);
|
|
485
|
-
m = "GFS4: " + m.split(/\n/).join(`
|
|
486
|
-
GFS4: `);
|
|
487
|
-
console.error(m);
|
|
488
|
-
};
|
|
489
|
-
if (!fs[gracefulQueue]) {
|
|
490
|
-
queue = global[gracefulQueue] || [];
|
|
491
|
-
publishQueue(fs, queue);
|
|
492
|
-
fs.close = function(fs$close) {
|
|
493
|
-
function close(fd, cb) {
|
|
494
|
-
return fs$close.call(fs, fd, function(err) {
|
|
495
|
-
if (!err) {
|
|
496
|
-
resetQueue();
|
|
497
|
-
}
|
|
498
|
-
if (typeof cb === "function")
|
|
499
|
-
cb.apply(this, arguments);
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
Object.defineProperty(close, previousSymbol, {
|
|
503
|
-
value: fs$close
|
|
504
|
-
});
|
|
505
|
-
return close;
|
|
506
|
-
}(fs.close);
|
|
507
|
-
fs.closeSync = function(fs$closeSync) {
|
|
508
|
-
function closeSync(fd) {
|
|
509
|
-
fs$closeSync.apply(fs, arguments);
|
|
510
|
-
resetQueue();
|
|
511
|
-
}
|
|
512
|
-
Object.defineProperty(closeSync, previousSymbol, {
|
|
513
|
-
value: fs$closeSync
|
|
514
|
-
});
|
|
515
|
-
return closeSync;
|
|
516
|
-
}(fs.closeSync);
|
|
517
|
-
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
|
|
518
|
-
process.on("exit", function() {
|
|
519
|
-
debug(fs[gracefulQueue]);
|
|
520
|
-
__require("assert").equal(fs[gracefulQueue].length, 0);
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
var queue;
|
|
525
|
-
if (!global[gracefulQueue]) {
|
|
526
|
-
publishQueue(global, fs[gracefulQueue]);
|
|
527
|
-
}
|
|
528
|
-
module.exports = patch(clone(fs));
|
|
529
|
-
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) {
|
|
530
|
-
module.exports = patch(fs);
|
|
531
|
-
fs.__patched = true;
|
|
532
|
-
}
|
|
533
|
-
function patch(fs2) {
|
|
534
|
-
polyfills(fs2);
|
|
535
|
-
fs2.gracefulify = patch;
|
|
536
|
-
fs2.createReadStream = createReadStream;
|
|
537
|
-
fs2.createWriteStream = createWriteStream;
|
|
538
|
-
var fs$readFile = fs2.readFile;
|
|
539
|
-
fs2.readFile = readFile;
|
|
540
|
-
function readFile(path, options, cb) {
|
|
541
|
-
if (typeof options === "function")
|
|
542
|
-
cb = options, options = null;
|
|
543
|
-
return go$readFile(path, options, cb);
|
|
544
|
-
function go$readFile(path2, options2, cb2, startTime) {
|
|
545
|
-
return fs$readFile(path2, options2, function(err) {
|
|
546
|
-
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
547
|
-
enqueue([go$readFile, [path2, options2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
548
|
-
else {
|
|
549
|
-
if (typeof cb2 === "function")
|
|
550
|
-
cb2.apply(this, arguments);
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
var fs$writeFile = fs2.writeFile;
|
|
556
|
-
fs2.writeFile = writeFile;
|
|
557
|
-
function writeFile(path, data, options, cb) {
|
|
558
|
-
if (typeof options === "function")
|
|
559
|
-
cb = options, options = null;
|
|
560
|
-
return go$writeFile(path, data, options, cb);
|
|
561
|
-
function go$writeFile(path2, data2, options2, cb2, startTime) {
|
|
562
|
-
return fs$writeFile(path2, data2, options2, function(err) {
|
|
563
|
-
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
564
|
-
enqueue([go$writeFile, [path2, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
565
|
-
else {
|
|
566
|
-
if (typeof cb2 === "function")
|
|
567
|
-
cb2.apply(this, arguments);
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
var fs$appendFile = fs2.appendFile;
|
|
573
|
-
if (fs$appendFile)
|
|
574
|
-
fs2.appendFile = appendFile;
|
|
575
|
-
function appendFile(path, data, options, cb) {
|
|
576
|
-
if (typeof options === "function")
|
|
577
|
-
cb = options, options = null;
|
|
578
|
-
return go$appendFile(path, data, options, cb);
|
|
579
|
-
function go$appendFile(path2, data2, options2, cb2, startTime) {
|
|
580
|
-
return fs$appendFile(path2, data2, options2, function(err) {
|
|
581
|
-
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
582
|
-
enqueue([go$appendFile, [path2, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
583
|
-
else {
|
|
584
|
-
if (typeof cb2 === "function")
|
|
585
|
-
cb2.apply(this, arguments);
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
var fs$copyFile = fs2.copyFile;
|
|
591
|
-
if (fs$copyFile)
|
|
592
|
-
fs2.copyFile = copyFile;
|
|
593
|
-
function copyFile(src, dest, flags, cb) {
|
|
594
|
-
if (typeof flags === "function") {
|
|
595
|
-
cb = flags;
|
|
596
|
-
flags = 0;
|
|
597
|
-
}
|
|
598
|
-
return go$copyFile(src, dest, flags, cb);
|
|
599
|
-
function go$copyFile(src2, dest2, flags2, cb2, startTime) {
|
|
600
|
-
return fs$copyFile(src2, dest2, flags2, function(err) {
|
|
601
|
-
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
602
|
-
enqueue([go$copyFile, [src2, dest2, flags2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
603
|
-
else {
|
|
604
|
-
if (typeof cb2 === "function")
|
|
605
|
-
cb2.apply(this, arguments);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
var fs$readdir = fs2.readdir;
|
|
611
|
-
fs2.readdir = readdir;
|
|
612
|
-
var noReaddirOptionVersions = /^v[0-5]\./;
|
|
613
|
-
function readdir(path, options, cb) {
|
|
614
|
-
if (typeof options === "function")
|
|
615
|
-
cb = options, options = null;
|
|
616
|
-
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path2, options2, cb2, startTime) {
|
|
617
|
-
return fs$readdir(path2, fs$readdirCallback(path2, options2, cb2, startTime));
|
|
618
|
-
} : function go$readdir2(path2, options2, cb2, startTime) {
|
|
619
|
-
return fs$readdir(path2, options2, fs$readdirCallback(path2, options2, cb2, startTime));
|
|
620
|
-
};
|
|
621
|
-
return go$readdir(path, options, cb);
|
|
622
|
-
function fs$readdirCallback(path2, options2, cb2, startTime) {
|
|
623
|
-
return function(err, files) {
|
|
624
|
-
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
625
|
-
enqueue([
|
|
626
|
-
go$readdir,
|
|
627
|
-
[path2, options2, cb2],
|
|
628
|
-
err,
|
|
629
|
-
startTime || Date.now(),
|
|
630
|
-
Date.now()
|
|
631
|
-
]);
|
|
632
|
-
else {
|
|
633
|
-
if (files && files.sort)
|
|
634
|
-
files.sort();
|
|
635
|
-
if (typeof cb2 === "function")
|
|
636
|
-
cb2.call(this, err, files);
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (process.version.substr(0, 4) === "v0.8") {
|
|
642
|
-
var legStreams = legacy(fs2);
|
|
643
|
-
ReadStream = legStreams.ReadStream;
|
|
644
|
-
WriteStream = legStreams.WriteStream;
|
|
645
|
-
}
|
|
646
|
-
var fs$ReadStream = fs2.ReadStream;
|
|
647
|
-
if (fs$ReadStream) {
|
|
648
|
-
ReadStream.prototype = Object.create(fs$ReadStream.prototype);
|
|
649
|
-
ReadStream.prototype.open = ReadStream$open;
|
|
650
|
-
}
|
|
651
|
-
var fs$WriteStream = fs2.WriteStream;
|
|
652
|
-
if (fs$WriteStream) {
|
|
653
|
-
WriteStream.prototype = Object.create(fs$WriteStream.prototype);
|
|
654
|
-
WriteStream.prototype.open = WriteStream$open;
|
|
655
|
-
}
|
|
656
|
-
Object.defineProperty(fs2, "ReadStream", {
|
|
657
|
-
get: function() {
|
|
658
|
-
return ReadStream;
|
|
659
|
-
},
|
|
660
|
-
set: function(val) {
|
|
661
|
-
ReadStream = val;
|
|
662
|
-
},
|
|
663
|
-
enumerable: true,
|
|
664
|
-
configurable: true
|
|
665
|
-
});
|
|
666
|
-
Object.defineProperty(fs2, "WriteStream", {
|
|
667
|
-
get: function() {
|
|
668
|
-
return WriteStream;
|
|
669
|
-
},
|
|
670
|
-
set: function(val) {
|
|
671
|
-
WriteStream = val;
|
|
672
|
-
},
|
|
673
|
-
enumerable: true,
|
|
674
|
-
configurable: true
|
|
675
|
-
});
|
|
676
|
-
var FileReadStream = ReadStream;
|
|
677
|
-
Object.defineProperty(fs2, "FileReadStream", {
|
|
678
|
-
get: function() {
|
|
679
|
-
return FileReadStream;
|
|
680
|
-
},
|
|
681
|
-
set: function(val) {
|
|
682
|
-
FileReadStream = val;
|
|
683
|
-
},
|
|
684
|
-
enumerable: true,
|
|
685
|
-
configurable: true
|
|
686
|
-
});
|
|
687
|
-
var FileWriteStream = WriteStream;
|
|
688
|
-
Object.defineProperty(fs2, "FileWriteStream", {
|
|
689
|
-
get: function() {
|
|
690
|
-
return FileWriteStream;
|
|
691
|
-
},
|
|
692
|
-
set: function(val) {
|
|
693
|
-
FileWriteStream = val;
|
|
694
|
-
},
|
|
695
|
-
enumerable: true,
|
|
696
|
-
configurable: true
|
|
697
|
-
});
|
|
698
|
-
function ReadStream(path, options) {
|
|
699
|
-
if (this instanceof ReadStream)
|
|
700
|
-
return fs$ReadStream.apply(this, arguments), this;
|
|
701
|
-
else
|
|
702
|
-
return ReadStream.apply(Object.create(ReadStream.prototype), arguments);
|
|
703
|
-
}
|
|
704
|
-
function ReadStream$open() {
|
|
705
|
-
var that = this;
|
|
706
|
-
open(that.path, that.flags, that.mode, function(err, fd) {
|
|
707
|
-
if (err) {
|
|
708
|
-
if (that.autoClose)
|
|
709
|
-
that.destroy();
|
|
710
|
-
that.emit("error", err);
|
|
711
|
-
} else {
|
|
712
|
-
that.fd = fd;
|
|
713
|
-
that.emit("open", fd);
|
|
714
|
-
that.read();
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
function WriteStream(path, options) {
|
|
719
|
-
if (this instanceof WriteStream)
|
|
720
|
-
return fs$WriteStream.apply(this, arguments), this;
|
|
721
|
-
else
|
|
722
|
-
return WriteStream.apply(Object.create(WriteStream.prototype), arguments);
|
|
723
|
-
}
|
|
724
|
-
function WriteStream$open() {
|
|
725
|
-
var that = this;
|
|
726
|
-
open(that.path, that.flags, that.mode, function(err, fd) {
|
|
727
|
-
if (err) {
|
|
728
|
-
that.destroy();
|
|
729
|
-
that.emit("error", err);
|
|
730
|
-
} else {
|
|
731
|
-
that.fd = fd;
|
|
732
|
-
that.emit("open", fd);
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
function createReadStream(path, options) {
|
|
737
|
-
return new fs2.ReadStream(path, options);
|
|
738
|
-
}
|
|
739
|
-
function createWriteStream(path, options) {
|
|
740
|
-
return new fs2.WriteStream(path, options);
|
|
741
|
-
}
|
|
742
|
-
var fs$open = fs2.open;
|
|
743
|
-
fs2.open = open;
|
|
744
|
-
function open(path, flags, mode, cb) {
|
|
745
|
-
if (typeof mode === "function")
|
|
746
|
-
cb = mode, mode = null;
|
|
747
|
-
return go$open(path, flags, mode, cb);
|
|
748
|
-
function go$open(path2, flags2, mode2, cb2, startTime) {
|
|
749
|
-
return fs$open(path2, flags2, mode2, function(err, fd) {
|
|
750
|
-
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
751
|
-
enqueue([go$open, [path2, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
752
|
-
else {
|
|
753
|
-
if (typeof cb2 === "function")
|
|
754
|
-
cb2.apply(this, arguments);
|
|
755
|
-
}
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
return fs2;
|
|
760
|
-
}
|
|
761
|
-
function enqueue(elem) {
|
|
762
|
-
debug("ENQUEUE", elem[0].name, elem[1]);
|
|
763
|
-
fs[gracefulQueue].push(elem);
|
|
764
|
-
retry();
|
|
765
|
-
}
|
|
766
|
-
var retryTimer;
|
|
767
|
-
function resetQueue() {
|
|
768
|
-
var now = Date.now();
|
|
769
|
-
for (var i = 0;i < fs[gracefulQueue].length; ++i) {
|
|
770
|
-
if (fs[gracefulQueue][i].length > 2) {
|
|
771
|
-
fs[gracefulQueue][i][3] = now;
|
|
772
|
-
fs[gracefulQueue][i][4] = now;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
retry();
|
|
776
|
-
}
|
|
777
|
-
function retry() {
|
|
778
|
-
clearTimeout(retryTimer);
|
|
779
|
-
retryTimer = undefined;
|
|
780
|
-
if (fs[gracefulQueue].length === 0)
|
|
781
|
-
return;
|
|
782
|
-
var elem = fs[gracefulQueue].shift();
|
|
783
|
-
var fn = elem[0];
|
|
784
|
-
var args = elem[1];
|
|
785
|
-
var err = elem[2];
|
|
786
|
-
var startTime = elem[3];
|
|
787
|
-
var lastTime = elem[4];
|
|
788
|
-
if (startTime === undefined) {
|
|
789
|
-
debug("RETRY", fn.name, args);
|
|
790
|
-
fn.apply(null, args);
|
|
791
|
-
} else if (Date.now() - startTime >= 60000) {
|
|
792
|
-
debug("TIMEOUT", fn.name, args);
|
|
793
|
-
var cb = args.pop();
|
|
794
|
-
if (typeof cb === "function")
|
|
795
|
-
cb.call(null, err);
|
|
796
|
-
} else {
|
|
797
|
-
var sinceAttempt = Date.now() - lastTime;
|
|
798
|
-
var sinceStart = Math.max(lastTime - startTime, 1);
|
|
799
|
-
var desiredDelay = Math.min(sinceStart * 1.2, 100);
|
|
800
|
-
if (sinceAttempt >= desiredDelay) {
|
|
801
|
-
debug("RETRY", fn.name, args);
|
|
802
|
-
fn.apply(null, args.concat([startTime]));
|
|
803
|
-
} else {
|
|
804
|
-
fs[gracefulQueue].push(elem);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
if (retryTimer === undefined) {
|
|
808
|
-
retryTimer = setTimeout(retry, 0);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
|
|
813
|
-
// node_modules/.bun/retry@0.12.0/node_modules/retry/lib/retry_operation.js
|
|
814
|
-
var require_retry_operation = __commonJS((exports, module) => {
|
|
815
|
-
function RetryOperation(timeouts, options) {
|
|
816
|
-
if (typeof options === "boolean") {
|
|
817
|
-
options = { forever: options };
|
|
818
|
-
}
|
|
819
|
-
this._originalTimeouts = JSON.parse(JSON.stringify(timeouts));
|
|
820
|
-
this._timeouts = timeouts;
|
|
821
|
-
this._options = options || {};
|
|
822
|
-
this._maxRetryTime = options && options.maxRetryTime || Infinity;
|
|
823
|
-
this._fn = null;
|
|
824
|
-
this._errors = [];
|
|
825
|
-
this._attempts = 1;
|
|
826
|
-
this._operationTimeout = null;
|
|
827
|
-
this._operationTimeoutCb = null;
|
|
828
|
-
this._timeout = null;
|
|
829
|
-
this._operationStart = null;
|
|
830
|
-
if (this._options.forever) {
|
|
831
|
-
this._cachedTimeouts = this._timeouts.slice(0);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
module.exports = RetryOperation;
|
|
835
|
-
RetryOperation.prototype.reset = function() {
|
|
836
|
-
this._attempts = 1;
|
|
837
|
-
this._timeouts = this._originalTimeouts;
|
|
838
|
-
};
|
|
839
|
-
RetryOperation.prototype.stop = function() {
|
|
840
|
-
if (this._timeout) {
|
|
841
|
-
clearTimeout(this._timeout);
|
|
842
|
-
}
|
|
843
|
-
this._timeouts = [];
|
|
844
|
-
this._cachedTimeouts = null;
|
|
845
|
-
};
|
|
846
|
-
RetryOperation.prototype.retry = function(err) {
|
|
847
|
-
if (this._timeout) {
|
|
848
|
-
clearTimeout(this._timeout);
|
|
849
|
-
}
|
|
850
|
-
if (!err) {
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
|
-
var currentTime = new Date().getTime();
|
|
854
|
-
if (err && currentTime - this._operationStart >= this._maxRetryTime) {
|
|
855
|
-
this._errors.unshift(new Error("RetryOperation timeout occurred"));
|
|
856
|
-
return false;
|
|
857
|
-
}
|
|
858
|
-
this._errors.push(err);
|
|
859
|
-
var timeout = this._timeouts.shift();
|
|
860
|
-
if (timeout === undefined) {
|
|
861
|
-
if (this._cachedTimeouts) {
|
|
862
|
-
this._errors.splice(this._errors.length - 1, this._errors.length);
|
|
863
|
-
this._timeouts = this._cachedTimeouts.slice(0);
|
|
864
|
-
timeout = this._timeouts.shift();
|
|
865
|
-
} else {
|
|
866
|
-
return false;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
var self = this;
|
|
870
|
-
var timer = setTimeout(function() {
|
|
871
|
-
self._attempts++;
|
|
872
|
-
if (self._operationTimeoutCb) {
|
|
873
|
-
self._timeout = setTimeout(function() {
|
|
874
|
-
self._operationTimeoutCb(self._attempts);
|
|
875
|
-
}, self._operationTimeout);
|
|
876
|
-
if (self._options.unref) {
|
|
877
|
-
self._timeout.unref();
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
self._fn(self._attempts);
|
|
881
|
-
}, timeout);
|
|
882
|
-
if (this._options.unref) {
|
|
883
|
-
timer.unref();
|
|
884
|
-
}
|
|
885
|
-
return true;
|
|
886
|
-
};
|
|
887
|
-
RetryOperation.prototype.attempt = function(fn, timeoutOps) {
|
|
888
|
-
this._fn = fn;
|
|
889
|
-
if (timeoutOps) {
|
|
890
|
-
if (timeoutOps.timeout) {
|
|
891
|
-
this._operationTimeout = timeoutOps.timeout;
|
|
892
|
-
}
|
|
893
|
-
if (timeoutOps.cb) {
|
|
894
|
-
this._operationTimeoutCb = timeoutOps.cb;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
var self = this;
|
|
898
|
-
if (this._operationTimeoutCb) {
|
|
899
|
-
this._timeout = setTimeout(function() {
|
|
900
|
-
self._operationTimeoutCb();
|
|
901
|
-
}, self._operationTimeout);
|
|
902
|
-
}
|
|
903
|
-
this._operationStart = new Date().getTime();
|
|
904
|
-
this._fn(this._attempts);
|
|
905
|
-
};
|
|
906
|
-
RetryOperation.prototype.try = function(fn) {
|
|
907
|
-
console.log("Using RetryOperation.try() is deprecated");
|
|
908
|
-
this.attempt(fn);
|
|
909
|
-
};
|
|
910
|
-
RetryOperation.prototype.start = function(fn) {
|
|
911
|
-
console.log("Using RetryOperation.start() is deprecated");
|
|
912
|
-
this.attempt(fn);
|
|
913
|
-
};
|
|
914
|
-
RetryOperation.prototype.start = RetryOperation.prototype.try;
|
|
915
|
-
RetryOperation.prototype.errors = function() {
|
|
916
|
-
return this._errors;
|
|
917
|
-
};
|
|
918
|
-
RetryOperation.prototype.attempts = function() {
|
|
919
|
-
return this._attempts;
|
|
920
|
-
};
|
|
921
|
-
RetryOperation.prototype.mainError = function() {
|
|
922
|
-
if (this._errors.length === 0) {
|
|
923
|
-
return null;
|
|
924
|
-
}
|
|
925
|
-
var counts = {};
|
|
926
|
-
var mainError = null;
|
|
927
|
-
var mainErrorCount = 0;
|
|
928
|
-
for (var i = 0;i < this._errors.length; i++) {
|
|
929
|
-
var error = this._errors[i];
|
|
930
|
-
var message = error.message;
|
|
931
|
-
var count = (counts[message] || 0) + 1;
|
|
932
|
-
counts[message] = count;
|
|
933
|
-
if (count >= mainErrorCount) {
|
|
934
|
-
mainError = error;
|
|
935
|
-
mainErrorCount = count;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
return mainError;
|
|
939
|
-
};
|
|
940
|
-
});
|
|
941
|
-
|
|
942
|
-
// node_modules/.bun/retry@0.12.0/node_modules/retry/lib/retry.js
|
|
943
|
-
var require_retry = __commonJS((exports) => {
|
|
944
|
-
var RetryOperation = require_retry_operation();
|
|
945
|
-
exports.operation = function(options) {
|
|
946
|
-
var timeouts = exports.timeouts(options);
|
|
947
|
-
return new RetryOperation(timeouts, {
|
|
948
|
-
forever: options && options.forever,
|
|
949
|
-
unref: options && options.unref,
|
|
950
|
-
maxRetryTime: options && options.maxRetryTime
|
|
951
|
-
});
|
|
952
|
-
};
|
|
953
|
-
exports.timeouts = function(options) {
|
|
954
|
-
if (options instanceof Array) {
|
|
955
|
-
return [].concat(options);
|
|
956
|
-
}
|
|
957
|
-
var opts = {
|
|
958
|
-
retries: 10,
|
|
959
|
-
factor: 2,
|
|
960
|
-
minTimeout: 1 * 1000,
|
|
961
|
-
maxTimeout: Infinity,
|
|
962
|
-
randomize: false
|
|
963
|
-
};
|
|
964
|
-
for (var key in options) {
|
|
965
|
-
opts[key] = options[key];
|
|
966
|
-
}
|
|
967
|
-
if (opts.minTimeout > opts.maxTimeout) {
|
|
968
|
-
throw new Error("minTimeout is greater than maxTimeout");
|
|
969
|
-
}
|
|
970
|
-
var timeouts = [];
|
|
971
|
-
for (var i = 0;i < opts.retries; i++) {
|
|
972
|
-
timeouts.push(this.createTimeout(i, opts));
|
|
973
|
-
}
|
|
974
|
-
if (options && options.forever && !timeouts.length) {
|
|
975
|
-
timeouts.push(this.createTimeout(i, opts));
|
|
976
|
-
}
|
|
977
|
-
timeouts.sort(function(a, b) {
|
|
978
|
-
return a - b;
|
|
979
|
-
});
|
|
980
|
-
return timeouts;
|
|
981
|
-
};
|
|
982
|
-
exports.createTimeout = function(attempt, opts) {
|
|
983
|
-
var random = opts.randomize ? Math.random() + 1 : 1;
|
|
984
|
-
var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt));
|
|
985
|
-
timeout = Math.min(timeout, opts.maxTimeout);
|
|
986
|
-
return timeout;
|
|
987
|
-
};
|
|
988
|
-
exports.wrap = function(obj, options, methods) {
|
|
989
|
-
if (options instanceof Array) {
|
|
990
|
-
methods = options;
|
|
991
|
-
options = null;
|
|
992
|
-
}
|
|
993
|
-
if (!methods) {
|
|
994
|
-
methods = [];
|
|
995
|
-
for (var key in obj) {
|
|
996
|
-
if (typeof obj[key] === "function") {
|
|
997
|
-
methods.push(key);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
for (var i = 0;i < methods.length; i++) {
|
|
1002
|
-
var method = methods[i];
|
|
1003
|
-
var original = obj[method];
|
|
1004
|
-
obj[method] = function retryWrapper(original2) {
|
|
1005
|
-
var op = exports.operation(options);
|
|
1006
|
-
var args = Array.prototype.slice.call(arguments, 1);
|
|
1007
|
-
var callback = args.pop();
|
|
1008
|
-
args.push(function(err) {
|
|
1009
|
-
if (op.retry(err)) {
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1012
|
-
if (err) {
|
|
1013
|
-
arguments[0] = op.mainError();
|
|
1014
|
-
}
|
|
1015
|
-
callback.apply(this, arguments);
|
|
1016
|
-
});
|
|
1017
|
-
op.attempt(function() {
|
|
1018
|
-
original2.apply(obj, args);
|
|
1019
|
-
});
|
|
1020
|
-
}.bind(obj, original);
|
|
1021
|
-
obj[method].options = options;
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
});
|
|
2
|
+
// src/tui.tsx
|
|
3
|
+
import { createComponent as _$createComponent4 } from "@opentui/solid";
|
|
4
|
+
import { Plugin } from "@opencode/plugin/tui";
|
|
5
|
+
import { Effect as Effect8, Option as Option10 } from "effect";
|
|
6
|
+
import { createSignal as createSignal3 } from "solid-js";
|
|
1025
7
|
|
|
1026
|
-
//
|
|
1027
|
-
|
|
1028
|
-
module.exports = [
|
|
1029
|
-
"SIGABRT",
|
|
1030
|
-
"SIGALRM",
|
|
1031
|
-
"SIGHUP",
|
|
1032
|
-
"SIGINT",
|
|
1033
|
-
"SIGTERM"
|
|
1034
|
-
];
|
|
1035
|
-
if (process.platform !== "win32") {
|
|
1036
|
-
module.exports.push("SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGUSR2", "SIGTRAP", "SIGSYS", "SIGQUIT", "SIGIOT");
|
|
1037
|
-
}
|
|
1038
|
-
if (process.platform === "linux") {
|
|
1039
|
-
module.exports.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT", "SIGUNUSED");
|
|
1040
|
-
}
|
|
1041
|
-
});
|
|
8
|
+
// src/adapters/Command.ts
|
|
9
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
1042
10
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
return process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
|
|
1048
|
-
};
|
|
1049
|
-
if (!processOk(process2)) {
|
|
1050
|
-
module.exports = function() {
|
|
1051
|
-
return function() {};
|
|
1052
|
-
};
|
|
1053
|
-
} else {
|
|
1054
|
-
assert = __require("assert");
|
|
1055
|
-
signals = require_signals();
|
|
1056
|
-
isWin = /^win/i.test(process2.platform);
|
|
1057
|
-
EE = __require("events");
|
|
1058
|
-
if (typeof EE !== "function") {
|
|
1059
|
-
EE = EE.EventEmitter;
|
|
1060
|
-
}
|
|
1061
|
-
if (process2.__signal_exit_emitter__) {
|
|
1062
|
-
emitter = process2.__signal_exit_emitter__;
|
|
1063
|
-
} else {
|
|
1064
|
-
emitter = process2.__signal_exit_emitter__ = new EE;
|
|
1065
|
-
emitter.count = 0;
|
|
1066
|
-
emitter.emitted = {};
|
|
1067
|
-
}
|
|
1068
|
-
if (!emitter.infinite) {
|
|
1069
|
-
emitter.setMaxListeners(Infinity);
|
|
1070
|
-
emitter.infinite = true;
|
|
1071
|
-
}
|
|
1072
|
-
module.exports = function(cb, opts) {
|
|
1073
|
-
if (!processOk(global.process)) {
|
|
1074
|
-
return function() {};
|
|
1075
|
-
}
|
|
1076
|
-
assert.equal(typeof cb, "function", "a callback must be provided for exit handler");
|
|
1077
|
-
if (loaded === false) {
|
|
1078
|
-
load();
|
|
1079
|
-
}
|
|
1080
|
-
var ev = "exit";
|
|
1081
|
-
if (opts && opts.alwaysLast) {
|
|
1082
|
-
ev = "afterexit";
|
|
1083
|
-
}
|
|
1084
|
-
var remove = function() {
|
|
1085
|
-
emitter.removeListener(ev, cb);
|
|
1086
|
-
if (emitter.listeners("exit").length === 0 && emitter.listeners("afterexit").length === 0) {
|
|
1087
|
-
unload();
|
|
1088
|
-
}
|
|
1089
|
-
};
|
|
1090
|
-
emitter.on(ev, cb);
|
|
1091
|
-
return remove;
|
|
1092
|
-
};
|
|
1093
|
-
unload = function unload2() {
|
|
1094
|
-
if (!loaded || !processOk(global.process)) {
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
loaded = false;
|
|
1098
|
-
signals.forEach(function(sig) {
|
|
1099
|
-
try {
|
|
1100
|
-
process2.removeListener(sig, sigListeners[sig]);
|
|
1101
|
-
} catch (er) {}
|
|
1102
|
-
});
|
|
1103
|
-
process2.emit = originalProcessEmit;
|
|
1104
|
-
process2.reallyExit = originalProcessReallyExit;
|
|
1105
|
-
emitter.count -= 1;
|
|
1106
|
-
};
|
|
1107
|
-
module.exports.unload = unload;
|
|
1108
|
-
emit = function emit2(event, code, signal) {
|
|
1109
|
-
if (emitter.emitted[event]) {
|
|
1110
|
-
return;
|
|
1111
|
-
}
|
|
1112
|
-
emitter.emitted[event] = true;
|
|
1113
|
-
emitter.emit(event, code, signal);
|
|
1114
|
-
};
|
|
1115
|
-
sigListeners = {};
|
|
1116
|
-
signals.forEach(function(sig) {
|
|
1117
|
-
sigListeners[sig] = function listener() {
|
|
1118
|
-
if (!processOk(global.process)) {
|
|
1119
|
-
return;
|
|
1120
|
-
}
|
|
1121
|
-
var listeners = process2.listeners(sig);
|
|
1122
|
-
if (listeners.length === emitter.count) {
|
|
1123
|
-
unload();
|
|
1124
|
-
emit("exit", null, sig);
|
|
1125
|
-
emit("afterexit", null, sig);
|
|
1126
|
-
if (isWin && sig === "SIGHUP") {
|
|
1127
|
-
sig = "SIGINT";
|
|
1128
|
-
}
|
|
1129
|
-
process2.kill(process2.pid, sig);
|
|
1130
|
-
}
|
|
1131
|
-
};
|
|
1132
|
-
});
|
|
1133
|
-
module.exports.signals = function() {
|
|
1134
|
-
return signals;
|
|
1135
|
-
};
|
|
1136
|
-
loaded = false;
|
|
1137
|
-
load = function load2() {
|
|
1138
|
-
if (loaded || !processOk(global.process)) {
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
loaded = true;
|
|
1142
|
-
emitter.count += 1;
|
|
1143
|
-
signals = signals.filter(function(sig) {
|
|
1144
|
-
try {
|
|
1145
|
-
process2.on(sig, sigListeners[sig]);
|
|
1146
|
-
return true;
|
|
1147
|
-
} catch (er) {
|
|
1148
|
-
return false;
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1151
|
-
process2.emit = processEmit;
|
|
1152
|
-
process2.reallyExit = processReallyExit;
|
|
1153
|
-
};
|
|
1154
|
-
module.exports.load = load;
|
|
1155
|
-
originalProcessReallyExit = process2.reallyExit;
|
|
1156
|
-
processReallyExit = function processReallyExit2(code) {
|
|
1157
|
-
if (!processOk(global.process)) {
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
process2.exitCode = code || 0;
|
|
1161
|
-
emit("exit", process2.exitCode, null);
|
|
1162
|
-
emit("afterexit", process2.exitCode, null);
|
|
1163
|
-
originalProcessReallyExit.call(process2, process2.exitCode);
|
|
1164
|
-
};
|
|
1165
|
-
originalProcessEmit = process2.emit;
|
|
1166
|
-
processEmit = function processEmit2(ev, arg) {
|
|
1167
|
-
if (ev === "exit" && processOk(global.process)) {
|
|
1168
|
-
if (arg !== undefined) {
|
|
1169
|
-
process2.exitCode = arg;
|
|
1170
|
-
}
|
|
1171
|
-
var ret = originalProcessEmit.apply(this, arguments);
|
|
1172
|
-
emit("exit", process2.exitCode, null);
|
|
1173
|
-
emit("afterexit", process2.exitCode, null);
|
|
1174
|
-
return ret;
|
|
1175
|
-
} else {
|
|
1176
|
-
return originalProcessEmit.apply(this, arguments);
|
|
1177
|
-
}
|
|
1178
|
-
};
|
|
1179
|
-
}
|
|
1180
|
-
var assert;
|
|
1181
|
-
var signals;
|
|
1182
|
-
var isWin;
|
|
1183
|
-
var EE;
|
|
1184
|
-
var emitter;
|
|
1185
|
-
var unload;
|
|
1186
|
-
var emit;
|
|
1187
|
-
var sigListeners;
|
|
1188
|
-
var loaded;
|
|
1189
|
-
var load;
|
|
1190
|
-
var originalProcessReallyExit;
|
|
1191
|
-
var processReallyExit;
|
|
1192
|
-
var originalProcessEmit;
|
|
1193
|
-
var processEmit;
|
|
1194
|
-
});
|
|
11
|
+
class CommandMissing extends Schema.TaggedError()("CommandMissing", {
|
|
12
|
+
command: Schema.String
|
|
13
|
+
}) {
|
|
14
|
+
}
|
|
1195
15
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
return fs.stat(file, (err, stat) => {
|
|
1203
|
-
if (err) {
|
|
1204
|
-
return callback(err);
|
|
1205
|
-
}
|
|
1206
|
-
callback(null, stat.mtime, cachedPrecision);
|
|
1207
|
-
});
|
|
1208
|
-
}
|
|
1209
|
-
const mtime = new Date(Math.ceil(Date.now() / 1000) * 1000 + 5);
|
|
1210
|
-
fs.utimes(file, mtime, mtime, (err) => {
|
|
1211
|
-
if (err) {
|
|
1212
|
-
return callback(err);
|
|
1213
|
-
}
|
|
1214
|
-
fs.stat(file, (err2, stat) => {
|
|
1215
|
-
if (err2) {
|
|
1216
|
-
return callback(err2);
|
|
1217
|
-
}
|
|
1218
|
-
const precision = stat.mtime.getTime() % 1000 === 0 ? "s" : "ms";
|
|
1219
|
-
Object.defineProperty(fs, cacheSymbol, { value: precision });
|
|
1220
|
-
callback(null, stat.mtime, precision);
|
|
1221
|
-
});
|
|
1222
|
-
});
|
|
1223
|
-
}
|
|
1224
|
-
function getMtime(precision) {
|
|
1225
|
-
let now = Date.now();
|
|
1226
|
-
if (precision === "s") {
|
|
1227
|
-
now = Math.ceil(now / 1000) * 1000;
|
|
1228
|
-
}
|
|
1229
|
-
return new Date(now);
|
|
1230
|
-
}
|
|
1231
|
-
exports.probe = probe;
|
|
1232
|
-
exports.getMtime = getMtime;
|
|
1233
|
-
});
|
|
16
|
+
class CommandFailed extends Schema.TaggedError()("CommandFailed", {
|
|
17
|
+
command: Schema.String,
|
|
18
|
+
exitCode: Schema.Int,
|
|
19
|
+
stderr: Schema.String
|
|
20
|
+
}) {
|
|
21
|
+
}
|
|
1234
22
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
return options.lockfilePath || `${file}.lock`;
|
|
1245
|
-
}
|
|
1246
|
-
function resolveCanonicalPath(file, options, callback) {
|
|
1247
|
-
if (!options.realpath) {
|
|
1248
|
-
return callback(null, path.resolve(file));
|
|
1249
|
-
}
|
|
1250
|
-
options.fs.realpath(file, callback);
|
|
1251
|
-
}
|
|
1252
|
-
function acquireLock(file, options, callback) {
|
|
1253
|
-
const lockfilePath = getLockFile(file, options);
|
|
1254
|
-
options.fs.mkdir(lockfilePath, (err) => {
|
|
1255
|
-
if (!err) {
|
|
1256
|
-
return mtimePrecision.probe(lockfilePath, options.fs, (err2, mtime, mtimePrecision2) => {
|
|
1257
|
-
if (err2) {
|
|
1258
|
-
options.fs.rmdir(lockfilePath, () => {});
|
|
1259
|
-
return callback(err2);
|
|
1260
|
-
}
|
|
1261
|
-
callback(null, mtime, mtimePrecision2);
|
|
1262
|
-
});
|
|
1263
|
-
}
|
|
1264
|
-
if (err.code !== "EEXIST") {
|
|
1265
|
-
return callback(err);
|
|
1266
|
-
}
|
|
1267
|
-
if (options.stale <= 0) {
|
|
1268
|
-
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
|
|
1269
|
-
}
|
|
1270
|
-
options.fs.stat(lockfilePath, (err2, stat) => {
|
|
1271
|
-
if (err2) {
|
|
1272
|
-
if (err2.code === "ENOENT") {
|
|
1273
|
-
return acquireLock(file, { ...options, stale: 0 }, callback);
|
|
1274
|
-
}
|
|
1275
|
-
return callback(err2);
|
|
1276
|
-
}
|
|
1277
|
-
if (!isLockStale(stat, options)) {
|
|
1278
|
-
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
|
|
1279
|
-
}
|
|
1280
|
-
removeLock(file, options, (err3) => {
|
|
1281
|
-
if (err3) {
|
|
1282
|
-
return callback(err3);
|
|
1283
|
-
}
|
|
1284
|
-
acquireLock(file, { ...options, stale: 0 }, callback);
|
|
1285
|
-
});
|
|
1286
|
-
});
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
function isLockStale(stat, options) {
|
|
1290
|
-
return stat.mtime.getTime() < Date.now() - options.stale;
|
|
1291
|
-
}
|
|
1292
|
-
function removeLock(file, options, callback) {
|
|
1293
|
-
options.fs.rmdir(getLockFile(file, options), (err) => {
|
|
1294
|
-
if (err && err.code !== "ENOENT") {
|
|
1295
|
-
return callback(err);
|
|
1296
|
-
}
|
|
1297
|
-
callback();
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
function updateLock(file, options) {
|
|
1301
|
-
const lock2 = locks[file];
|
|
1302
|
-
if (lock2.updateTimeout) {
|
|
1303
|
-
return;
|
|
1304
|
-
}
|
|
1305
|
-
lock2.updateDelay = lock2.updateDelay || options.update;
|
|
1306
|
-
lock2.updateTimeout = setTimeout(() => {
|
|
1307
|
-
lock2.updateTimeout = null;
|
|
1308
|
-
options.fs.stat(lock2.lockfilePath, (err, stat) => {
|
|
1309
|
-
const isOverThreshold = lock2.lastUpdate + options.stale < Date.now();
|
|
1310
|
-
if (err) {
|
|
1311
|
-
if (err.code === "ENOENT" || isOverThreshold) {
|
|
1312
|
-
return setLockAsCompromised(file, lock2, Object.assign(err, { code: "ECOMPROMISED" }));
|
|
1313
|
-
}
|
|
1314
|
-
lock2.updateDelay = 1000;
|
|
1315
|
-
return updateLock(file, options);
|
|
1316
|
-
}
|
|
1317
|
-
const isMtimeOurs = lock2.mtime.getTime() === stat.mtime.getTime();
|
|
1318
|
-
if (!isMtimeOurs) {
|
|
1319
|
-
return setLockAsCompromised(file, lock2, Object.assign(new Error("Unable to update lock within the stale threshold"), { code: "ECOMPROMISED" }));
|
|
1320
|
-
}
|
|
1321
|
-
const mtime = mtimePrecision.getMtime(lock2.mtimePrecision);
|
|
1322
|
-
options.fs.utimes(lock2.lockfilePath, mtime, mtime, (err2) => {
|
|
1323
|
-
const isOverThreshold2 = lock2.lastUpdate + options.stale < Date.now();
|
|
1324
|
-
if (lock2.released) {
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
if (err2) {
|
|
1328
|
-
if (err2.code === "ENOENT" || isOverThreshold2) {
|
|
1329
|
-
return setLockAsCompromised(file, lock2, Object.assign(err2, { code: "ECOMPROMISED" }));
|
|
1330
|
-
}
|
|
1331
|
-
lock2.updateDelay = 1000;
|
|
1332
|
-
return updateLock(file, options);
|
|
1333
|
-
}
|
|
1334
|
-
lock2.mtime = mtime;
|
|
1335
|
-
lock2.lastUpdate = Date.now();
|
|
1336
|
-
lock2.updateDelay = null;
|
|
1337
|
-
updateLock(file, options);
|
|
1338
|
-
});
|
|
1339
|
-
});
|
|
1340
|
-
}, lock2.updateDelay);
|
|
1341
|
-
if (lock2.updateTimeout.unref) {
|
|
1342
|
-
lock2.updateTimeout.unref();
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
function setLockAsCompromised(file, lock2, err) {
|
|
1346
|
-
lock2.released = true;
|
|
1347
|
-
if (lock2.updateTimeout) {
|
|
1348
|
-
clearTimeout(lock2.updateTimeout);
|
|
1349
|
-
}
|
|
1350
|
-
if (locks[file] === lock2) {
|
|
1351
|
-
delete locks[file];
|
|
1352
|
-
}
|
|
1353
|
-
lock2.options.onCompromised(err);
|
|
1354
|
-
}
|
|
1355
|
-
function lock(file, options, callback) {
|
|
1356
|
-
options = {
|
|
1357
|
-
stale: 1e4,
|
|
1358
|
-
update: null,
|
|
1359
|
-
realpath: true,
|
|
1360
|
-
retries: 0,
|
|
1361
|
-
fs,
|
|
1362
|
-
onCompromised: (err) => {
|
|
1363
|
-
throw err;
|
|
1364
|
-
},
|
|
1365
|
-
...options
|
|
1366
|
-
};
|
|
1367
|
-
options.retries = options.retries || 0;
|
|
1368
|
-
options.retries = typeof options.retries === "number" ? { retries: options.retries } : options.retries;
|
|
1369
|
-
options.stale = Math.max(options.stale || 0, 2000);
|
|
1370
|
-
options.update = options.update == null ? options.stale / 2 : options.update || 0;
|
|
1371
|
-
options.update = Math.max(Math.min(options.update, options.stale / 2), 1000);
|
|
1372
|
-
resolveCanonicalPath(file, options, (err, file2) => {
|
|
1373
|
-
if (err) {
|
|
1374
|
-
return callback(err);
|
|
1375
|
-
}
|
|
1376
|
-
const operation = retry.operation(options.retries);
|
|
1377
|
-
operation.attempt(() => {
|
|
1378
|
-
acquireLock(file2, options, (err2, mtime, mtimePrecision2) => {
|
|
1379
|
-
if (operation.retry(err2)) {
|
|
1380
|
-
return;
|
|
1381
|
-
}
|
|
1382
|
-
if (err2) {
|
|
1383
|
-
return callback(operation.mainError());
|
|
1384
|
-
}
|
|
1385
|
-
const lock2 = locks[file2] = {
|
|
1386
|
-
lockfilePath: getLockFile(file2, options),
|
|
1387
|
-
mtime,
|
|
1388
|
-
mtimePrecision: mtimePrecision2,
|
|
1389
|
-
options,
|
|
1390
|
-
lastUpdate: Date.now()
|
|
1391
|
-
};
|
|
1392
|
-
updateLock(file2, options);
|
|
1393
|
-
callback(null, (releasedCallback) => {
|
|
1394
|
-
if (lock2.released) {
|
|
1395
|
-
return releasedCallback && releasedCallback(Object.assign(new Error("Lock is already released"), { code: "ERELEASED" }));
|
|
1396
|
-
}
|
|
1397
|
-
unlock(file2, { ...options, realpath: false }, releasedCallback);
|
|
1398
|
-
});
|
|
1399
|
-
});
|
|
1400
|
-
});
|
|
1401
|
-
});
|
|
1402
|
-
}
|
|
1403
|
-
function unlock(file, options, callback) {
|
|
1404
|
-
options = {
|
|
1405
|
-
fs,
|
|
1406
|
-
realpath: true,
|
|
1407
|
-
...options
|
|
1408
|
-
};
|
|
1409
|
-
resolveCanonicalPath(file, options, (err, file2) => {
|
|
1410
|
-
if (err) {
|
|
1411
|
-
return callback(err);
|
|
1412
|
-
}
|
|
1413
|
-
const lock2 = locks[file2];
|
|
1414
|
-
if (!lock2) {
|
|
1415
|
-
return callback(Object.assign(new Error("Lock is not acquired/owned by you"), { code: "ENOTACQUIRED" }));
|
|
1416
|
-
}
|
|
1417
|
-
lock2.updateTimeout && clearTimeout(lock2.updateTimeout);
|
|
1418
|
-
lock2.released = true;
|
|
1419
|
-
delete locks[file2];
|
|
1420
|
-
removeLock(file2, options, callback);
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
function check(file, options, callback) {
|
|
1424
|
-
options = {
|
|
1425
|
-
stale: 1e4,
|
|
1426
|
-
realpath: true,
|
|
1427
|
-
fs,
|
|
1428
|
-
...options
|
|
1429
|
-
};
|
|
1430
|
-
options.stale = Math.max(options.stale || 0, 2000);
|
|
1431
|
-
resolveCanonicalPath(file, options, (err, file2) => {
|
|
1432
|
-
if (err) {
|
|
1433
|
-
return callback(err);
|
|
1434
|
-
}
|
|
1435
|
-
options.fs.stat(getLockFile(file2, options), (err2, stat) => {
|
|
1436
|
-
if (err2) {
|
|
1437
|
-
return err2.code === "ENOENT" ? callback(null, false) : callback(err2);
|
|
1438
|
-
}
|
|
1439
|
-
return callback(null, !isLockStale(stat, options));
|
|
1440
|
-
});
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
function getLocks() {
|
|
1444
|
-
return locks;
|
|
1445
|
-
}
|
|
1446
|
-
onExit(() => {
|
|
1447
|
-
for (const file in locks) {
|
|
1448
|
-
const options = locks[file].options;
|
|
1449
|
-
try {
|
|
1450
|
-
options.fs.rmdirSync(getLockFile(file, options));
|
|
1451
|
-
} catch (e) {}
|
|
1452
|
-
}
|
|
23
|
+
class CommandRunner extends Context.Service()("opencode-pr-tracker/CommandRunner") {
|
|
24
|
+
}
|
|
25
|
+
var isMissingExecutable = Schema.is(Schema.Struct({ code: Schema.Literal("ENOENT") }));
|
|
26
|
+
function spawn(command, args, cwd) {
|
|
27
|
+
const process2 = Bun.spawn([command, ...args], {
|
|
28
|
+
cwd,
|
|
29
|
+
stderr: "pipe",
|
|
30
|
+
stdin: "ignore",
|
|
31
|
+
stdout: "pipe"
|
|
1453
32
|
});
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
var require_adapter = __commonJS((exports, module) => {
|
|
1462
|
-
var fs = require_graceful_fs();
|
|
1463
|
-
function createSyncFs(fs2) {
|
|
1464
|
-
const methods = ["mkdir", "realpath", "stat", "rmdir", "utimes"];
|
|
1465
|
-
const newFs = { ...fs2 };
|
|
1466
|
-
methods.forEach((method) => {
|
|
1467
|
-
newFs[method] = (...args) => {
|
|
1468
|
-
const callback = args.pop();
|
|
1469
|
-
let ret;
|
|
1470
|
-
try {
|
|
1471
|
-
ret = fs2[`${method}Sync`](...args);
|
|
1472
|
-
} catch (err) {
|
|
1473
|
-
return callback(err);
|
|
1474
|
-
}
|
|
1475
|
-
callback(null, ret);
|
|
1476
|
-
};
|
|
1477
|
-
});
|
|
1478
|
-
return newFs;
|
|
1479
|
-
}
|
|
1480
|
-
function toPromise(method) {
|
|
1481
|
-
return (...args) => new Promise((resolve, reject) => {
|
|
1482
|
-
args.push((err, result) => {
|
|
1483
|
-
if (err) {
|
|
1484
|
-
reject(err);
|
|
1485
|
-
} else {
|
|
1486
|
-
resolve(result);
|
|
1487
|
-
}
|
|
1488
|
-
});
|
|
1489
|
-
method(...args);
|
|
1490
|
-
});
|
|
1491
|
-
}
|
|
1492
|
-
function toSync(method) {
|
|
1493
|
-
return (...args) => {
|
|
1494
|
-
let err;
|
|
1495
|
-
let result;
|
|
1496
|
-
args.push((_err, _result) => {
|
|
1497
|
-
err = _err;
|
|
1498
|
-
result = _result;
|
|
1499
|
-
});
|
|
1500
|
-
method(...args);
|
|
1501
|
-
if (err) {
|
|
1502
|
-
throw err;
|
|
1503
|
-
}
|
|
33
|
+
return {
|
|
34
|
+
completed: async () => {
|
|
35
|
+
const result = await Promise.all([
|
|
36
|
+
new Response(process2.stdout).text(),
|
|
37
|
+
new Response(process2.stderr).text(),
|
|
38
|
+
process2.exited
|
|
39
|
+
]);
|
|
1504
40
|
return result;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if (typeof options.retries === "number" && options.retries > 0 || options.retries && typeof options.retries.retries === "number" && options.retries.retries > 0) {
|
|
1511
|
-
throw Object.assign(new Error("Cannot use retries with the sync api"), { code: "ESYNC" });
|
|
1512
|
-
}
|
|
1513
|
-
return options;
|
|
1514
|
-
}
|
|
1515
|
-
module.exports = {
|
|
1516
|
-
toPromise,
|
|
1517
|
-
toSync,
|
|
1518
|
-
toSyncOptions
|
|
41
|
+
},
|
|
42
|
+
kill: () => {
|
|
43
|
+
process2.kill();
|
|
44
|
+
},
|
|
45
|
+
running: () => process2.exitCode === null
|
|
1519
46
|
};
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
}
|
|
1543
|
-
function checkSync(file, options) {
|
|
1544
|
-
return toSync(lockfile.check)(file, toSyncOptions(options));
|
|
1545
|
-
}
|
|
1546
|
-
module.exports = lock;
|
|
1547
|
-
module.exports.lock = lock;
|
|
1548
|
-
module.exports.unlock = unlock;
|
|
1549
|
-
module.exports.lockSync = lockSync;
|
|
1550
|
-
module.exports.unlockSync = unlockSync;
|
|
1551
|
-
module.exports.check = check;
|
|
1552
|
-
module.exports.checkSync = checkSync;
|
|
1553
|
-
});
|
|
1554
|
-
|
|
1555
|
-
// src/tui.tsx
|
|
1556
|
-
import { memo as _$memo } from "@opentui/solid";
|
|
1557
|
-
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
1558
|
-
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
1559
|
-
import { setProp as _$setProp } from "@opentui/solid";
|
|
1560
|
-
import { effect as _$effect } from "@opentui/solid";
|
|
1561
|
-
import { insert as _$insert } from "@opentui/solid";
|
|
1562
|
-
import { createElement as _$createElement } from "@opentui/solid";
|
|
1563
|
-
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
1564
|
-
import { TextAttributes } from "@opentui/core";
|
|
1565
|
-
import { createSignal, onCleanup } from "solid-js";
|
|
47
|
+
}
|
|
48
|
+
function start(command, args, cwd) {
|
|
49
|
+
const started = Effect.try({
|
|
50
|
+
catch: (cause) => isMissingExecutable(cause) ? new CommandMissing({ command }) : new CommandFailed({ command, exitCode: -1, stderr: String(cause) }),
|
|
51
|
+
try: () => spawn(command, args, cwd)
|
|
52
|
+
});
|
|
53
|
+
return Effect.acquireRelease(started, (child) => Effect.sync(() => {
|
|
54
|
+
if (child.running())
|
|
55
|
+
child.kill();
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function output(command, child) {
|
|
59
|
+
return Effect.gen(function* () {
|
|
60
|
+
const [stdout, stderr, exitCode] = yield* Effect.promise(child.completed);
|
|
61
|
+
if (exitCode === 0)
|
|
62
|
+
return stdout;
|
|
63
|
+
return yield* new CommandFailed({ command, exitCode, stderr });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
var layer = Layer.succeed(CommandRunner, CommandRunner.of({
|
|
67
|
+
run: (command, args, cwd) => Effect.scoped(Effect.flatMap(start(command, args, cwd), (child) => output(command, child)))
|
|
68
|
+
}));
|
|
1566
69
|
|
|
1567
|
-
// src/
|
|
1568
|
-
import {
|
|
70
|
+
// src/rpc.ts
|
|
71
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
72
|
+
import { Schema as Schema5 } from "effect";
|
|
1569
73
|
|
|
1570
|
-
// src/
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
74
|
+
// src/domain/PullRequest.ts
|
|
75
|
+
import { Option, Result, Schema as Schema2 } from "effect";
|
|
76
|
+
var canonicalSegment = /^(?!\.{1,2}$)[a-z0-9._-]+$/u;
|
|
77
|
+
var pullRequestUrl = /^(?:https:\/\/)?github\.com\/(?<owner>[\w.-]+)\/(?<repository>[\w.-]+)\/pull\/(?<number>\d+)$/iu;
|
|
78
|
+
var decimal = /^\d+$/u;
|
|
79
|
+
var printableAscii = /^[\u0021-\u007E]*$/u;
|
|
80
|
+
var Segment = Schema2.String.check(Schema2.isPattern(canonicalSegment));
|
|
81
|
+
var PullRequestNumber = Schema2.Int.check(Schema2.isBetween({ maximum: Number.MAX_SAFE_INTEGER, minimum: 1 }));
|
|
1574
82
|
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
};
|
|
1583
|
-
var segmentPattern = /^[A-Za-z0-9._-]+$/;
|
|
1584
|
-
function parsePullRequestUrl(input) {
|
|
1585
|
-
if (input.trim() !== input)
|
|
1586
|
-
return invalidPullRequestUrl;
|
|
1587
|
-
if (input.includes("\\"))
|
|
1588
|
-
return invalidPullRequestUrl;
|
|
1589
|
-
if (!input.startsWith("https://"))
|
|
1590
|
-
return invalidPullRequestUrl;
|
|
1591
|
-
const authorityEnd = input.indexOf("/", "https://".length);
|
|
1592
|
-
if (authorityEnd === -1)
|
|
1593
|
-
return invalidPullRequestUrl;
|
|
1594
|
-
if (input.slice("https://".length, authorityEnd).toLowerCase() !== "github.com") {
|
|
1595
|
-
return invalidPullRequestUrl;
|
|
1596
|
-
}
|
|
1597
|
-
const rawPath = input.slice(authorityEnd).split(/[?#]/, 1).join("");
|
|
1598
|
-
for (const segment of rawPath.split("/")) {
|
|
1599
|
-
let decoded;
|
|
1600
|
-
try {
|
|
1601
|
-
decoded = decodeURIComponent(segment);
|
|
1602
|
-
} catch {
|
|
1603
|
-
return invalidPullRequestUrl;
|
|
1604
|
-
}
|
|
1605
|
-
if (decoded === "." || decoded === "..")
|
|
1606
|
-
return invalidPullRequestUrl;
|
|
83
|
+
class PullRequestRef extends Schema2.Class("PullRequestRef")({
|
|
84
|
+
number: PullRequestNumber,
|
|
85
|
+
owner: Segment,
|
|
86
|
+
repository: Segment
|
|
87
|
+
}) {
|
|
88
|
+
get url() {
|
|
89
|
+
return `https://github.com/${this.owner}/${this.repository}/pull/${String(this.number)}`;
|
|
1607
90
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
parsed = new URL(input);
|
|
1611
|
-
} catch {
|
|
1612
|
-
return invalidPullRequestUrl;
|
|
91
|
+
get label() {
|
|
92
|
+
return `${this.owner}/${this.repository}#${String(this.number)}`;
|
|
1613
93
|
}
|
|
1614
|
-
if (parsed.protocol !== "https:" || parsed.hostname !== "github.com" || parsed.username !== "" || parsed.password !== "" || parsed.port !== "" || parsed.search !== "" || parsed.hash !== "") {
|
|
1615
|
-
return invalidPullRequestUrl;
|
|
1616
|
-
}
|
|
1617
|
-
const segments = parsed.pathname.split("/");
|
|
1618
|
-
if (segments.length !== 5 || segments[0] !== "" || segments[3] !== "pull") {
|
|
1619
|
-
return invalidPullRequestUrl;
|
|
1620
|
-
}
|
|
1621
|
-
const rawOwner = segments[1];
|
|
1622
|
-
const rawRepository = segments[2];
|
|
1623
|
-
const numberText = segments[4];
|
|
1624
|
-
if (rawOwner === undefined || rawRepository === undefined || numberText === undefined || !segmentPattern.test(rawOwner) || !segmentPattern.test(rawRepository) || !/^\d+$/.test(numberText)) {
|
|
1625
|
-
return invalidPullRequestUrl;
|
|
1626
|
-
}
|
|
1627
|
-
const number = Number(numberText);
|
|
1628
|
-
if (!Number.isSafeInteger(number) || number <= 0)
|
|
1629
|
-
return invalidPullRequestUrl;
|
|
1630
|
-
const owner = rawOwner.toLowerCase();
|
|
1631
|
-
const repository = rawRepository.toLowerCase();
|
|
1632
|
-
const url = `https://github.com/${owner}/${repository}/pull/${number}`;
|
|
1633
|
-
const value = { url, owner, repository, number };
|
|
1634
|
-
return { ok: true, value };
|
|
1635
94
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
95
|
+
|
|
96
|
+
class InvalidPullRequestUrl extends Schema2.TaggedError()("InvalidPullRequestUrl", { input: Schema2.String }) {
|
|
1638
97
|
}
|
|
1639
98
|
|
|
1640
|
-
|
|
1641
|
-
var invalidGitHubResponse = {
|
|
1642
|
-
ok: false,
|
|
1643
|
-
error: {
|
|
1644
|
-
tag: "InvalidGitHubResponse",
|
|
1645
|
-
message: "GitHub returned an invalid pull request response"
|
|
1646
|
-
}
|
|
1647
|
-
};
|
|
1648
|
-
var githubBatchLimitExceeded = {
|
|
1649
|
-
ok: false,
|
|
1650
|
-
error: {
|
|
1651
|
-
tag: "GitHubBatchLimitExceeded",
|
|
1652
|
-
limit: 20,
|
|
1653
|
-
message: "GitHub batch cannot contain more than 20 pull requests"
|
|
1654
|
-
}
|
|
1655
|
-
};
|
|
1656
|
-
var checkRunPending = new Set(["QUEUED", "IN_PROGRESS", "WAITING", "PENDING"]);
|
|
1657
|
-
var checkRunPassed = new Set(["SUCCESS"]);
|
|
1658
|
-
var checkRunFailed = new Set(["FAILURE", "CANCELLED", "TIMED_OUT", "ACTION_REQUIRED", "STARTUP_FAILURE", "STALE"]);
|
|
1659
|
-
var checkRunIgnored = new Set(["NEUTRAL", "SKIPPED"]);
|
|
1660
|
-
var statusContextPending = new Set(["EXPECTED", "PENDING"]);
|
|
1661
|
-
var statusContextPassed = new Set(["SUCCESS"]);
|
|
1662
|
-
var statusContextFailed = new Set(["ERROR", "FAILURE"]);
|
|
1663
|
-
var maximumPullRequestsPerBatch = 20;
|
|
1664
|
-
var pullRequestSelection = `__typename ... on PullRequest { title state url mergedAt mergeable statusCheckRollup { contexts(first: 1) { checkRunCount statusContextCount checkRunCountsByState { state count } statusContextCountsByState { state count } } } }`;
|
|
1665
|
-
function isRecord(value) {
|
|
1666
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
99
|
+
class InvalidPullRequestInput extends Schema2.TaggedError()("InvalidPullRequestInput", { input: Schema2.String }) {
|
|
1667
100
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
101
|
+
var decodeRef = Schema2.decodeUnknownOption(PullRequestRef);
|
|
102
|
+
var decodeNumber = Schema2.decodeUnknownOption(PullRequestNumber);
|
|
103
|
+
function samePullRequest(left, right) {
|
|
104
|
+
return left.url === right.url;
|
|
105
|
+
}
|
|
106
|
+
function parsePullRequestUrl(input) {
|
|
107
|
+
const match = printableAscii.test(input) ? pullRequestUrl.exec(input) : null;
|
|
108
|
+
const groups = match === null ? {} : match.groups ?? {};
|
|
109
|
+
const candidate = {
|
|
110
|
+
number: Number(groups["number"] ?? Number.NaN),
|
|
111
|
+
owner: (groups["owner"] ?? "").toLowerCase(),
|
|
112
|
+
repository: (groups["repository"] ?? "").toLowerCase()
|
|
1678
113
|
};
|
|
114
|
+
return Result.fromOption(decodeRef(candidate), () => new InvalidPullRequestUrl({ input }));
|
|
1679
115
|
}
|
|
1680
|
-
function
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
return "passed";
|
|
1687
|
-
if (states.ignored?.has(state))
|
|
1688
|
-
return "ignored";
|
|
1689
|
-
return;
|
|
116
|
+
function parsePullRequestInput(input) {
|
|
117
|
+
const reference = parsePullRequestUrl(input);
|
|
118
|
+
if (Result.isSuccess(reference))
|
|
119
|
+
return Result.succeed({ _tag: "Reference", ref: reference.success });
|
|
120
|
+
const number = decimal.test(input) ? decodeNumber(Number(input)) : Option.none();
|
|
121
|
+
return Result.fromOption(number, () => new InvalidPullRequestInput({ input })).pipe(Result.map((value) => ({ _tag: "Number", number: value })));
|
|
1690
122
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
123
|
+
|
|
124
|
+
// src/domain/Snapshot.ts
|
|
125
|
+
import { Duration, Match, Schema as Schema3 } from "effect";
|
|
126
|
+
var Ci = Schema3.Literals(["passed", "pending", "failed", "none"]);
|
|
127
|
+
var Mergeability = Schema3.Literals(["mergeable", "conflicting", "unknown"]);
|
|
128
|
+
var PullRequestState = Schema3.Union([
|
|
129
|
+
Schema3.TaggedStruct("Open", {
|
|
130
|
+
behind: Schema3.Boolean,
|
|
131
|
+
ci: Ci,
|
|
132
|
+
draft: Schema3.Boolean,
|
|
133
|
+
mergeability: Mergeability
|
|
134
|
+
}),
|
|
135
|
+
Schema3.TaggedStruct("Merged", {}),
|
|
136
|
+
Schema3.TaggedStruct("Closed", {})
|
|
137
|
+
]);
|
|
138
|
+
var Snapshot = Schema3.Struct({
|
|
139
|
+
ref: PullRequestRef,
|
|
140
|
+
state: PullRequestState,
|
|
141
|
+
title: Schema3.String
|
|
142
|
+
});
|
|
143
|
+
var Diagnostic = Schema3.Literals([
|
|
144
|
+
"GitHubCliMissing",
|
|
145
|
+
"AuthenticationRequired",
|
|
146
|
+
"GitHubUnavailable",
|
|
147
|
+
"NotFound",
|
|
148
|
+
"InvalidResponse"
|
|
149
|
+
]);
|
|
150
|
+
var Status = Schema3.Union([
|
|
151
|
+
Schema3.TaggedStruct("Pending", {}),
|
|
152
|
+
Schema3.TaggedStruct("Fresh", { snapshot: Snapshot }),
|
|
153
|
+
Schema3.TaggedStruct("Stale", {
|
|
154
|
+
diagnostic: Diagnostic,
|
|
155
|
+
failingSince: Schema3.Int,
|
|
156
|
+
snapshot: Snapshot
|
|
157
|
+
}),
|
|
158
|
+
Schema3.TaggedStruct("Unavailable", { diagnostic: Diagnostic })
|
|
159
|
+
]);
|
|
160
|
+
var staleLimit = Duration.minutes(5);
|
|
161
|
+
var pending = { _tag: "Pending" };
|
|
162
|
+
function succeeded(snapshot) {
|
|
163
|
+
return { _tag: "Fresh", snapshot };
|
|
1711
164
|
}
|
|
1712
|
-
function
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
const checkRuns = aggregateCounts(contexts.checkRunCountsByState, contexts.checkRunCount, {
|
|
1719
|
-
passed: checkRunPassed,
|
|
1720
|
-
pending: checkRunPending,
|
|
1721
|
-
failed: checkRunFailed,
|
|
1722
|
-
ignored: checkRunIgnored
|
|
165
|
+
function failed(status, diagnostic, now) {
|
|
166
|
+
return Match.valueTags(status, {
|
|
167
|
+
Fresh: ({ snapshot }) => ({ _tag: "Stale", diagnostic, failingSince: now, snapshot }),
|
|
168
|
+
Pending: () => ({ _tag: "Unavailable", diagnostic }),
|
|
169
|
+
Stale: ({ failingSince, snapshot }) => now - failingSince >= Duration.toMillis(staleLimit) ? { _tag: "Unavailable", diagnostic } : { _tag: "Stale", diagnostic, failingSince, snapshot },
|
|
170
|
+
Unavailable: () => ({ _tag: "Unavailable", diagnostic })
|
|
1723
171
|
});
|
|
1724
|
-
if (!checkRuns.ok)
|
|
1725
|
-
return checkRuns;
|
|
1726
|
-
const statusContexts = aggregateCounts(contexts.statusContextCountsByState, contexts.statusContextCount, {
|
|
1727
|
-
passed: statusContextPassed,
|
|
1728
|
-
pending: statusContextPending,
|
|
1729
|
-
failed: statusContextFailed
|
|
1730
|
-
});
|
|
1731
|
-
if (!statusContexts.ok)
|
|
1732
|
-
return statusContexts;
|
|
1733
|
-
const buckets = new Set([...checkRuns.value, ...statusContexts.value]);
|
|
1734
|
-
if (buckets.has("failed"))
|
|
1735
|
-
return { ok: true, value: "failed" };
|
|
1736
|
-
if (buckets.has("pending"))
|
|
1737
|
-
return { ok: true, value: "pending" };
|
|
1738
|
-
if (buckets.has("passed"))
|
|
1739
|
-
return { ok: true, value: "passed" };
|
|
1740
|
-
return { ok: true, value: "none" };
|
|
1741
172
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
173
|
+
|
|
174
|
+
// src/domain/StackLayout.ts
|
|
175
|
+
import { Array as Arr, Option as Option2, Schema as Schema4 } from "effect";
|
|
176
|
+
var Membership = Schema4.Union([
|
|
177
|
+
Schema4.TaggedStruct("Standalone", {}),
|
|
178
|
+
Schema4.TaggedStruct("Stack", {
|
|
179
|
+
id: Schema4.String,
|
|
180
|
+
members: Schema4.NonEmptyArray(PullRequestRef)
|
|
181
|
+
})
|
|
182
|
+
]);
|
|
183
|
+
var stackOf = (entry) => Option2.filter(entry.membership, (membership) => membership._tag === "Stack");
|
|
184
|
+
var urlsOf = (stack) => stack.members.map((member) => member.url);
|
|
185
|
+
function reportsById(entries) {
|
|
186
|
+
const byId = new Map;
|
|
187
|
+
for (const [index, entry] of entries.entries()) {
|
|
188
|
+
for (const stack of Option2.toArray(stackOf(entry))) {
|
|
189
|
+
byId.set(stack.id, [...byId.get(stack.id) ?? [], { index, stack, url: entry.ref.url }]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return byId;
|
|
1744
193
|
}
|
|
1745
|
-
function
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
case "UNKNOWN":
|
|
1752
|
-
return { ok: true, value: "unknown" };
|
|
1753
|
-
default:
|
|
1754
|
-
return invalidGitHubResponse;
|
|
194
|
+
function claims(byId) {
|
|
195
|
+
const claimed = new Map;
|
|
196
|
+
for (const [id, reports] of byId) {
|
|
197
|
+
for (const url of reports.flatMap((report) => urlsOf(report.stack))) {
|
|
198
|
+
claimed.set(url, new Set([...claimed.get(url) ?? [], id]));
|
|
199
|
+
}
|
|
1755
200
|
}
|
|
201
|
+
return claimed;
|
|
1756
202
|
}
|
|
1757
|
-
function
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
return invalidGitHubResponse;
|
|
1768
|
-
}
|
|
1769
|
-
if (input.state === "MERGED" && typeof input.mergedAt !== "string")
|
|
1770
|
-
return invalidGitHubResponse;
|
|
1771
|
-
if (input.state !== "MERGED" && input.mergedAt !== null)
|
|
1772
|
-
return invalidGitHubResponse;
|
|
1773
|
-
if (typeof input.url !== "string")
|
|
1774
|
-
return invalidGitHubResponse;
|
|
1775
|
-
const responseUrl = parsePullRequestUrl(input.url);
|
|
1776
|
-
if (!responseUrl.ok || !samePullRequest(responseUrl.value, pullRequest))
|
|
1777
|
-
return invalidGitHubResponse;
|
|
1778
|
-
const ci = parseStatusCheckRollup(input.statusCheckRollup);
|
|
1779
|
-
if (!ci.ok)
|
|
1780
|
-
return ci;
|
|
1781
|
-
const mergeability = parseMergeability(input.mergeable);
|
|
1782
|
-
if (!mergeability.ok)
|
|
1783
|
-
return mergeability;
|
|
1784
|
-
let state;
|
|
1785
|
-
switch (input.state) {
|
|
1786
|
-
case "OPEN":
|
|
1787
|
-
state = { tag: "Open", ci: ci.value, mergeability: mergeability.value };
|
|
1788
|
-
break;
|
|
1789
|
-
case "MERGED":
|
|
1790
|
-
state = { tag: "Merged" };
|
|
1791
|
-
break;
|
|
1792
|
-
case "CLOSED":
|
|
1793
|
-
state = { tag: "Closed" };
|
|
1794
|
-
break;
|
|
1795
|
-
default:
|
|
1796
|
-
return casesHandled(input.state);
|
|
1797
|
-
}
|
|
1798
|
-
return {
|
|
1799
|
-
ok: true,
|
|
1800
|
-
value: {
|
|
1801
|
-
tag: "Available",
|
|
1802
|
-
pullRequest,
|
|
1803
|
-
title: input.title,
|
|
1804
|
-
state,
|
|
1805
|
-
stale: false
|
|
1806
|
-
}
|
|
1807
|
-
};
|
|
203
|
+
function agrees(reports, entries, claimed) {
|
|
204
|
+
const members = Option2.match(Arr.head(reports), {
|
|
205
|
+
onNone: () => [],
|
|
206
|
+
onSome: (report) => urlsOf(report.stack)
|
|
207
|
+
});
|
|
208
|
+
const listed = new Set(members);
|
|
209
|
+
const attachedMembers = entries.filter((entry) => listed.has(entry.ref.url));
|
|
210
|
+
return listed.size === members.length && reports.every((report) => urlsOf(report.stack).join(`
|
|
211
|
+
`) === members.join(`
|
|
212
|
+
`)) && members.every((url) => (claimed.get(url) ?? new Set).size === 1) && attachedMembers.length === reports.length;
|
|
1808
213
|
}
|
|
1809
|
-
function
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
214
|
+
function placements(reports) {
|
|
215
|
+
const placed = reports.map((report) => ({
|
|
216
|
+
index: report.index,
|
|
217
|
+
position: urlsOf(report.stack).indexOf(report.url),
|
|
218
|
+
size: report.stack.members.length
|
|
219
|
+
}));
|
|
220
|
+
const adjacent = Arr.zipWith(placed, placed.slice(1), (before, after) => after.index === before.index + 1 && after.position > before.position);
|
|
221
|
+
const ordered = placed.every((member) => member.position >= 0) && adjacent.every(Boolean);
|
|
222
|
+
return ordered ? Option2.some(placed) : Option2.none();
|
|
1813
223
|
}
|
|
1814
|
-
function
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
224
|
+
function consistentStacks(entries) {
|
|
225
|
+
const byId = reportsById(entries);
|
|
226
|
+
const claimed = claims(byId);
|
|
227
|
+
const places = new Map;
|
|
228
|
+
for (const reports of byId.values()) {
|
|
229
|
+
const placed = agrees(reports, entries, claimed) ? placements(reports) : Option2.none();
|
|
230
|
+
for (const group of Option2.toArray(placed)) {
|
|
231
|
+
for (const [step, current] of group.entries()) {
|
|
232
|
+
places.set(current.index, {
|
|
233
|
+
attached: group.length,
|
|
234
|
+
current,
|
|
235
|
+
first: step === 0,
|
|
236
|
+
last: step === group.length - 1,
|
|
237
|
+
previous: Arr.get(group, step - 1)
|
|
238
|
+
});
|
|
239
|
+
}
|
|
1823
240
|
}
|
|
1824
|
-
const match = /^pr([0-9]+)$/.exec(error.path[0]);
|
|
1825
|
-
if (match === null)
|
|
1826
|
-
return invalidGitHubResponse;
|
|
1827
|
-
const index = Number(match[1]);
|
|
1828
|
-
if (!Number.isInteger(index) || index < 0 || index >= size)
|
|
1829
|
-
return invalidGitHubResponse;
|
|
1830
|
-
aliases.add(index);
|
|
1831
241
|
}
|
|
1832
|
-
return
|
|
1833
|
-
}
|
|
1834
|
-
function parseBatchResponse(input, pullRequests) {
|
|
1835
|
-
if (!isRecord(input) || !isRecord(input.data))
|
|
1836
|
-
return invalidGitHubResponse;
|
|
1837
|
-
const data = input.data;
|
|
1838
|
-
const errorAliases = parseGraphqlErrorAliases(input.errors, pullRequests.length);
|
|
1839
|
-
if (!errorAliases.ok)
|
|
1840
|
-
return errorAliases;
|
|
1841
|
-
return {
|
|
1842
|
-
ok: true,
|
|
1843
|
-
value: pullRequests.map((pullRequest, index) => errorAliases.value.has(index) ? invalidGitHubResponse : parseResponse(data[`pr${index}`], pullRequest))
|
|
1844
|
-
};
|
|
242
|
+
return places;
|
|
1845
243
|
}
|
|
1846
|
-
function
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
return true;
|
|
1856
|
-
const stderr = failure.stderr.toLowerCase();
|
|
1857
|
-
return authenticationFailureMarkers.some((marker) => stderr.includes(marker));
|
|
1858
|
-
}
|
|
1859
|
-
function classifyProcessFailure(cause) {
|
|
1860
|
-
const failure = parseProcessExecutionFailed(cause);
|
|
1861
|
-
if (failure?.code === "ENOENT") {
|
|
1862
|
-
return { tag: "GitHubCliMissing", message: "GitHub CLI is not installed", cause };
|
|
1863
|
-
}
|
|
1864
|
-
if (failure && isAuthenticationFailure(failure)) {
|
|
1865
|
-
return { tag: "GitHubAuthenticationRequired", message: "GitHub CLI authentication required", cause };
|
|
1866
|
-
}
|
|
1867
|
-
return { tag: "GitHubUnavailable", message: "GitHub status unavailable", cause };
|
|
244
|
+
function marker(place) {
|
|
245
|
+
const { current } = place;
|
|
246
|
+
if (place.attached === 1 && current.size > 1)
|
|
247
|
+
return "middle";
|
|
248
|
+
if (place.first && current.position === 0)
|
|
249
|
+
return "first";
|
|
250
|
+
if (place.last && current.position === current.size - 1)
|
|
251
|
+
return "last";
|
|
252
|
+
return "middle";
|
|
1868
253
|
}
|
|
1869
|
-
function
|
|
1870
|
-
if (!
|
|
1871
|
-
return;
|
|
1872
|
-
return
|
|
254
|
+
function connector(place) {
|
|
255
|
+
if (!place.last)
|
|
256
|
+
return "continues";
|
|
257
|
+
return place.current.position < place.current.size - 1 ? "open" : "none";
|
|
1873
258
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
...options.cwd ? { cwd: options.cwd } : {}
|
|
1879
|
-
}, (error, stdout, stderr) => {
|
|
1880
|
-
if (error) {
|
|
1881
|
-
reject({
|
|
1882
|
-
tag: "ProcessExecutionFailed",
|
|
1883
|
-
code: error.code ?? null,
|
|
1884
|
-
stderr,
|
|
1885
|
-
stdout,
|
|
1886
|
-
cause: error
|
|
1887
|
-
});
|
|
1888
|
-
return;
|
|
1889
|
-
}
|
|
1890
|
-
resolve({ stdout });
|
|
259
|
+
function stackRows(entry, place) {
|
|
260
|
+
const skipped = Option2.match(place.previous, {
|
|
261
|
+
onNone: () => 0,
|
|
262
|
+
onSome: (before) => place.current.position - before.position - 1
|
|
1891
263
|
});
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
return { ok: true, value: [] };
|
|
1898
|
-
if (pullRequests.length > maximumPullRequestsPerBatch)
|
|
1899
|
-
return githubBatchLimitExceeded;
|
|
1900
|
-
const query = createBatchQuery(pullRequests.length);
|
|
1901
|
-
const args = ["api", "graphql", "--method", "POST", "-f", `query=${query}`];
|
|
1902
|
-
for (const [index, pullRequest] of pullRequests.entries()) {
|
|
1903
|
-
args.push("-f", `url${index}=${pullRequest.url}`);
|
|
1904
|
-
}
|
|
1905
|
-
let stdout;
|
|
1906
|
-
let processFailure;
|
|
1907
|
-
try {
|
|
1908
|
-
const output = await runner("gh", args, options);
|
|
1909
|
-
stdout = output.stdout;
|
|
1910
|
-
} catch (cause) {
|
|
1911
|
-
if (isCancellation(cause, options.signal)) {
|
|
1912
|
-
return {
|
|
1913
|
-
ok: false,
|
|
1914
|
-
error: {
|
|
1915
|
-
tag: "GitHubCancelled",
|
|
1916
|
-
message: "GitHub status request cancelled",
|
|
1917
|
-
cause
|
|
1918
|
-
}
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
processFailure = classifyProcessFailure(cause);
|
|
1922
|
-
const partialStdout = processFailureStdout(cause);
|
|
1923
|
-
if (partialStdout !== undefined) {
|
|
1924
|
-
stdout = partialStdout;
|
|
1925
|
-
} else {
|
|
1926
|
-
return { ok: false, error: processFailure };
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
let decoded;
|
|
1930
|
-
try {
|
|
1931
|
-
decoded = JSON.parse(stdout);
|
|
1932
|
-
} catch {
|
|
1933
|
-
return processFailure === undefined ? invalidGitHubResponse : { ok: false, error: processFailure };
|
|
1934
|
-
}
|
|
1935
|
-
const parsed = parseBatchResponse(decoded, pullRequests);
|
|
1936
|
-
if (processFailure === undefined || parsed.ok)
|
|
1937
|
-
return parsed;
|
|
1938
|
-
return { ok: false, error: processFailure };
|
|
1939
|
-
}
|
|
264
|
+
const row = {
|
|
265
|
+
_tag: "PullRequest",
|
|
266
|
+
connector: connector(place),
|
|
267
|
+
entry,
|
|
268
|
+
marker: marker(place)
|
|
1940
269
|
};
|
|
270
|
+
return skipped > 0 ? [{ _tag: "Gap", count: skipped }, row] : [row];
|
|
1941
271
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
};
|
|
1948
|
-
var diagnosticLabels = {
|
|
1949
|
-
GitHubCliMissing: "install gh",
|
|
1950
|
-
GitHubAuthenticationRequired: "run gh auth login",
|
|
1951
|
-
GitHubUnavailable: "GitHub unavailable",
|
|
1952
|
-
InvalidGitHubResponse: "invalid GitHub response"
|
|
1953
|
-
};
|
|
1954
|
-
function stateAppearance(state) {
|
|
1955
|
-
switch (state.tag) {
|
|
1956
|
-
case "Open": {
|
|
1957
|
-
switch (state.mergeability) {
|
|
1958
|
-
case "conflicting":
|
|
1959
|
-
return { tone: "red", label: "merge conflict", strikethrough: false };
|
|
1960
|
-
case "mergeable":
|
|
1961
|
-
case "unknown":
|
|
1962
|
-
return openAppearances[state.ci];
|
|
1963
|
-
default:
|
|
1964
|
-
return casesHandled(state.mergeability);
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
case "Merged":
|
|
1968
|
-
return { tone: "purple", label: "merged", strikethrough: true };
|
|
1969
|
-
case "Closed":
|
|
1970
|
-
return { tone: "red", label: "closed", strikethrough: true };
|
|
1971
|
-
default:
|
|
1972
|
-
return casesHandled(state);
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
function statusAppearance(status) {
|
|
1976
|
-
if (status.tag === "Unavailable") {
|
|
1977
|
-
return {
|
|
1978
|
-
tone: "gray",
|
|
1979
|
-
label: status.diagnostic === undefined ? "status unavailable" : diagnosticLabels[status.diagnostic],
|
|
1980
|
-
strikethrough: false
|
|
1981
|
-
};
|
|
1982
|
-
}
|
|
1983
|
-
const appearance = stateAppearance(status.state);
|
|
1984
|
-
return status.stale ? { ...appearance, label: `${appearance.label} (stale; ${diagnosticLabels[status.diagnostic]})` } : appearance;
|
|
272
|
+
function layout(entries) {
|
|
273
|
+
const places = consistentStacks(entries);
|
|
274
|
+
return entries.flatMap((entry, index) => Option2.match(Option2.fromNullishOr(places.get(index)), {
|
|
275
|
+
onNone: () => [{ _tag: "PullRequest", connector: "none", entry, marker: "bullet" }],
|
|
276
|
+
onSome: (place) => stackRows(entry, place)
|
|
277
|
+
}));
|
|
1985
278
|
}
|
|
1986
279
|
|
|
1987
|
-
// src/
|
|
1988
|
-
var
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
};
|
|
1995
|
-
var
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
};
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
return cause instanceof Error && cause.name === "AbortError";
|
|
2007
|
-
}
|
|
2008
|
-
function parseRepositoryPullRequest(stdout, number) {
|
|
2009
|
-
let decoded;
|
|
2010
|
-
try {
|
|
2011
|
-
decoded = JSON.parse(stdout);
|
|
2012
|
-
} catch {
|
|
2013
|
-
return { ok: false, error: repositoryResolutionFailed };
|
|
2014
|
-
}
|
|
2015
|
-
if (decoded === null || typeof decoded !== "object" || !("url" in decoded) || typeof decoded.url !== "string" || decoded.url === "") {
|
|
2016
|
-
return { ok: false, error: repositoryResolutionFailed };
|
|
2017
|
-
}
|
|
2018
|
-
const repositoryUrl = decoded.url.endsWith("/") ? decoded.url.slice(0, -1) : decoded.url;
|
|
2019
|
-
const pullRequest = parsePullRequestUrl(`${repositoryUrl}/pull/${number}`);
|
|
2020
|
-
return pullRequest.ok ? pullRequest : { ok: false, error: repositoryResolutionFailed };
|
|
280
|
+
// src/rpc.ts
|
|
281
|
+
var Layout = Schema5.Literals(["default", "compact"]);
|
|
282
|
+
var EntryView = Schema5.Struct({
|
|
283
|
+
attachedAt: Schema5.Int,
|
|
284
|
+
membership: Schema5.NullOr(Membership),
|
|
285
|
+
ref: PullRequestRef,
|
|
286
|
+
status: Status
|
|
287
|
+
});
|
|
288
|
+
var View = Schema5.Struct({
|
|
289
|
+
entries: Schema5.Array(EntryView),
|
|
290
|
+
layout: Layout,
|
|
291
|
+
sessionID: Schema5.String
|
|
292
|
+
});
|
|
293
|
+
var Session = Schema5.Struct({ sessionID: Schema5.String });
|
|
294
|
+
var Target = Schema5.Struct({ sessionID: Schema5.String, target: Schema5.String });
|
|
295
|
+
var Changed = Schema5.Struct({ message: Schema5.String, view: View });
|
|
296
|
+
var Rejected = Schema5.Struct({ message: Schema5.String });
|
|
297
|
+
function portable(schema) {
|
|
298
|
+
return { "~standard": Schema5.toStandardSchemaV1(Schema5.toEncoded(schema))["~standard"] };
|
|
2021
299
|
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
300
|
+
var PullRequestTracker = Rpc.define({
|
|
301
|
+
events: {
|
|
302
|
+
updated: { schema: portable(View) }
|
|
303
|
+
},
|
|
304
|
+
id: "opencode-pr-tracker",
|
|
305
|
+
methods: {
|
|
306
|
+
attach: {
|
|
307
|
+
errors: { rejected: portable(Rejected) },
|
|
308
|
+
input: portable(Target),
|
|
309
|
+
output: portable(Changed)
|
|
310
|
+
},
|
|
311
|
+
detach: {
|
|
312
|
+
errors: { rejected: portable(Rejected) },
|
|
313
|
+
input: portable(Target),
|
|
314
|
+
output: portable(Changed)
|
|
315
|
+
},
|
|
316
|
+
list: {
|
|
317
|
+
errors: { rejected: portable(Rejected) },
|
|
318
|
+
input: portable(Session),
|
|
319
|
+
output: portable(View)
|
|
320
|
+
},
|
|
321
|
+
refresh: {
|
|
322
|
+
errors: { rejected: portable(Rejected) },
|
|
323
|
+
input: portable(Session),
|
|
324
|
+
output: portable(View)
|
|
325
|
+
}
|
|
2042
326
|
}
|
|
2043
|
-
|
|
2044
|
-
}
|
|
327
|
+
});
|
|
2045
328
|
|
|
2046
|
-
// src/
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
tag: "InvalidStateFile",
|
|
2057
|
-
message: "The session pull request state file is invalid"
|
|
2058
|
-
}
|
|
2059
|
-
};
|
|
2060
|
-
var lockStaleMilliseconds = 1e4;
|
|
2061
|
-
var lockUpdateMilliseconds = 2000;
|
|
2062
|
-
function stateUnavailable(operation, message, cause) {
|
|
2063
|
-
return { tag: "StateUnavailable", operation, message, cause };
|
|
2064
|
-
}
|
|
2065
|
-
function isRecord2(value) {
|
|
2066
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
329
|
+
// src/tui/Actions.ts
|
|
330
|
+
import { Effect as Effect2, Option as Option3 } from "effect";
|
|
331
|
+
var placeholder = "github.com/owner/repository/pull/123, or 123";
|
|
332
|
+
function inSession(terminal, body) {
|
|
333
|
+
return Effect2.suspend(() => Option3.match(terminal.session(), {
|
|
334
|
+
onNone: () => Effect2.sync(() => {
|
|
335
|
+
terminal.notify("warning", "Open a session first.");
|
|
336
|
+
}),
|
|
337
|
+
onSome: body
|
|
338
|
+
}));
|
|
2067
339
|
}
|
|
2068
|
-
function
|
|
2069
|
-
|
|
2070
|
-
|
|
340
|
+
function report(terminal, effect, notice) {
|
|
341
|
+
return Effect2.map(Effect2.result(effect), (result) => {
|
|
342
|
+
if (result._tag === "Failure") {
|
|
343
|
+
terminal.notify("error", result.failure.message);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
for (const [variant, message] of Option3.toArray(notice(result.success))) {
|
|
347
|
+
terminal.notify(variant, message);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
2071
350
|
}
|
|
2072
|
-
function
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
return invalidStateFile;
|
|
2089
|
-
const attachedAt = new Date(item.attachedAt);
|
|
2090
|
-
if (Number.isNaN(attachedAt.valueOf()) || attachedAt.toISOString() !== item.attachedAt)
|
|
2091
|
-
return invalidStateFile;
|
|
2092
|
-
seen.add(item.url);
|
|
2093
|
-
attachments.push({ pullRequest: parsed.value, attachedAt: item.attachedAt });
|
|
2094
|
-
}
|
|
2095
|
-
return { ok: true, value: attachments };
|
|
351
|
+
function pick({ terminal, tracker }, sessionID, title) {
|
|
352
|
+
return Effect2.result(tracker.list(sessionID)).pipe(Effect2.flatMap((result) => {
|
|
353
|
+
if (result._tag === "Failure") {
|
|
354
|
+
terminal.notify("error", result.failure.message);
|
|
355
|
+
return Effect2.succeedNone;
|
|
356
|
+
}
|
|
357
|
+
const { entries } = result.success;
|
|
358
|
+
if (entries.length === 0) {
|
|
359
|
+
terminal.notify("info", "No pull requests are attached.");
|
|
360
|
+
return Effect2.succeedNone;
|
|
361
|
+
}
|
|
362
|
+
return terminal.choose(title, entries.map((entry) => ({
|
|
363
|
+
title: entry.ref.label,
|
|
364
|
+
value: entry.ref
|
|
365
|
+
})));
|
|
366
|
+
}));
|
|
2096
367
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
368
|
+
var changed = (outcome) => Option3.some(["success", outcome.message]);
|
|
369
|
+
function syncedNotice(view) {
|
|
370
|
+
const count = view.entries.length;
|
|
371
|
+
const failed = view.entries.filter((entry) => entry.status._tag === "Stale" || entry.status._tag === "Unavailable").length;
|
|
372
|
+
if (count === 0)
|
|
373
|
+
return Option3.some(["info", "No pull requests are attached."]);
|
|
374
|
+
const synced = `Synced ${String(count)} ${count === 1 ? "pull request" : "pull requests"}`;
|
|
375
|
+
return Option3.some(failed === 0 ? ["success", `${synced}.`] : ["warning", `${synced}; ${String(failed)} could not be refreshed.`]);
|
|
2099
376
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
377
|
+
var typedTarget = (text) => Option3.filter(Option3.some(text.trim()), (trimmed) => trimmed !== "");
|
|
378
|
+
function attach(services, input) {
|
|
379
|
+
const { terminal, tracker } = services;
|
|
380
|
+
return inSession(terminal, (sessionID) => Option3.match(Option3.flatMap(input, typedTarget), {
|
|
381
|
+
onNone: () => Effect2.map(terminal.prompt("Attach pull request", placeholder), Option3.flatMap(typedTarget)),
|
|
382
|
+
onSome: (text) => Effect2.succeedSome(text)
|
|
383
|
+
}).pipe(Effect2.flatMap(Option3.match({
|
|
384
|
+
onNone: () => Effect2.void,
|
|
385
|
+
onSome: (target) => report(terminal, tracker.attach(sessionID, target), changed)
|
|
386
|
+
}))));
|
|
2102
387
|
}
|
|
2103
|
-
function
|
|
2104
|
-
|
|
2105
|
-
|
|
388
|
+
function withPicked(services, action) {
|
|
389
|
+
return inSession(services.terminal, (sessionID) => pick(services, sessionID, action.title).pipe(Effect2.flatMap(Option3.match({
|
|
390
|
+
onNone: () => Effect2.void,
|
|
391
|
+
onSome: (ref) => report(services.terminal, action.act(sessionID, ref), action.notice)
|
|
392
|
+
}))));
|
|
2106
393
|
}
|
|
2107
|
-
function
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
async function withLock(sessionID, operation) {
|
|
2133
|
-
const lock = await acquireLock(sessionID);
|
|
2134
|
-
if (!lock.ok)
|
|
2135
|
-
return lock;
|
|
2136
|
-
let result;
|
|
2137
|
-
try {
|
|
2138
|
-
result = await operation();
|
|
2139
|
-
} catch (cause) {
|
|
2140
|
-
await lock.value.release().catch(() => {
|
|
2141
|
-
return;
|
|
2142
|
-
});
|
|
2143
|
-
throw cause;
|
|
2144
|
-
}
|
|
2145
|
-
try {
|
|
2146
|
-
await lock.value.release();
|
|
2147
|
-
} catch (cause) {
|
|
2148
|
-
return {
|
|
2149
|
-
ok: false,
|
|
2150
|
-
error: stateUnavailable("write", "Unable to unlock the session pull request state", cause)
|
|
2151
|
-
};
|
|
2152
|
-
}
|
|
2153
|
-
const compromise = lock.value.compromised();
|
|
2154
|
-
if (compromise !== undefined) {
|
|
2155
|
-
return {
|
|
2156
|
-
ok: false,
|
|
2157
|
-
error: stateUnavailable("write", "The session pull request state lock was compromised", compromise)
|
|
2158
|
-
};
|
|
2159
|
-
}
|
|
2160
|
-
return result;
|
|
2161
|
-
}
|
|
2162
|
-
async function readExisting(sessionID) {
|
|
2163
|
-
const path = join(directory, fileName(sessionID));
|
|
2164
|
-
let content;
|
|
2165
|
-
try {
|
|
2166
|
-
content = await readFile(path, "utf8");
|
|
2167
|
-
} catch (cause) {
|
|
2168
|
-
if (isMissingFile(cause))
|
|
2169
|
-
return { ok: true, value: undefined };
|
|
2170
|
-
return {
|
|
2171
|
-
ok: false,
|
|
2172
|
-
error: stateUnavailable("read", "Unable to read the session pull request state", cause)
|
|
2173
|
-
};
|
|
2174
|
-
}
|
|
2175
|
-
let decoded;
|
|
2176
|
-
try {
|
|
2177
|
-
decoded = JSON.parse(content);
|
|
2178
|
-
} catch {
|
|
2179
|
-
return invalidStateFile;
|
|
2180
|
-
}
|
|
2181
|
-
return parseState(decoded);
|
|
2182
|
-
}
|
|
2183
|
-
async function read(sessionID) {
|
|
2184
|
-
const result = await readExisting(sessionID);
|
|
2185
|
-
if (!result.ok)
|
|
2186
|
-
return result;
|
|
2187
|
-
return { ok: true, value: result.value ?? [] };
|
|
2188
|
-
}
|
|
2189
|
-
async function write(sessionID, attachments) {
|
|
2190
|
-
const destination = join(directory, fileName(sessionID));
|
|
2191
|
-
const temporary = `${destination}.${process.pid}.${randomUUID()}.tmp`;
|
|
2192
|
-
const state = {
|
|
2193
|
-
version: 1,
|
|
2194
|
-
pullRequests: attachments.map((attachment) => ({
|
|
2195
|
-
url: attachment.pullRequest.url,
|
|
2196
|
-
attachedAt: attachment.attachedAt
|
|
2197
|
-
}))
|
|
2198
|
-
};
|
|
2199
|
-
try {
|
|
2200
|
-
await mkdir(directory, { recursive: true });
|
|
2201
|
-
await writeFile(temporary, `${JSON.stringify(state, null, 2)}
|
|
2202
|
-
`, { mode: 384 });
|
|
2203
|
-
await rename(temporary, destination);
|
|
2204
|
-
return { ok: true, value: undefined };
|
|
2205
|
-
} catch (cause) {
|
|
2206
|
-
await rm(temporary, { force: true }).catch(() => {
|
|
2207
|
-
return;
|
|
2208
|
-
});
|
|
2209
|
-
return {
|
|
2210
|
-
ok: false,
|
|
2211
|
-
error: stateUnavailable("write", "Unable to write the session pull request state", cause)
|
|
2212
|
-
};
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
394
|
+
function actions(services) {
|
|
395
|
+
const { terminal, tracker } = services;
|
|
396
|
+
return {
|
|
397
|
+
attach: (input) => attach(services, input),
|
|
398
|
+
detach: withPicked(services, {
|
|
399
|
+
act: (sessionID, ref) => tracker.detach(sessionID, ref.url),
|
|
400
|
+
notice: changed,
|
|
401
|
+
title: "Detach pull request"
|
|
402
|
+
}),
|
|
403
|
+
open: withPicked(services, {
|
|
404
|
+
act: (_, ref) => services.open(ref.url),
|
|
405
|
+
notice: Option3.none,
|
|
406
|
+
title: "Open pull request"
|
|
407
|
+
}),
|
|
408
|
+
openPullRequest: (ref) => report(terminal, services.open(ref.url), Option3.none),
|
|
409
|
+
sync: inSession(terminal, (sessionID) => report(terminal, tracker.refresh(sessionID), syncedNotice))
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/tui/Background.ts
|
|
414
|
+
import { Effect as Effect3, Fiber } from "effect";
|
|
415
|
+
function background() {
|
|
416
|
+
const running = new Set;
|
|
2215
417
|
return {
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
return current;
|
|
2222
|
-
if (current.value.some((attachment) => attachment.pullRequest.url === pullRequest.url)) {
|
|
2223
|
-
return { ok: true, value: "already_attached" };
|
|
2224
|
-
}
|
|
2225
|
-
if (current.value.length >= maximumPullRequestsPerSession) {
|
|
2226
|
-
return {
|
|
2227
|
-
ok: false,
|
|
2228
|
-
error: {
|
|
2229
|
-
tag: "AttachmentLimitReached",
|
|
2230
|
-
limit: maximumPullRequestsPerSession,
|
|
2231
|
-
message: "A session can track at most 20 pull requests"
|
|
2232
|
-
}
|
|
2233
|
-
};
|
|
2234
|
-
}
|
|
2235
|
-
const written = await write(sessionID, [...current.value, { pullRequest, attachedAt: now().toISOString() }]);
|
|
2236
|
-
if (!written.ok)
|
|
2237
|
-
return written;
|
|
2238
|
-
return { ok: true, value: "added" };
|
|
2239
|
-
});
|
|
2240
|
-
},
|
|
2241
|
-
async detach(sessionID, pullRequest) {
|
|
2242
|
-
return withLock(sessionID, async () => {
|
|
2243
|
-
const current = await read(sessionID);
|
|
2244
|
-
if (!current.ok)
|
|
2245
|
-
return current;
|
|
2246
|
-
const next = current.value.filter((attachment) => attachment.pullRequest.url !== pullRequest.url);
|
|
2247
|
-
if (next.length === current.value.length)
|
|
2248
|
-
return { ok: true, value: "absent" };
|
|
2249
|
-
const written = await write(sessionID, next);
|
|
2250
|
-
if (!written.ok)
|
|
2251
|
-
return written;
|
|
2252
|
-
return { ok: true, value: "removed" };
|
|
2253
|
-
});
|
|
2254
|
-
},
|
|
2255
|
-
async detachByNumber(sessionID, number) {
|
|
2256
|
-
return withLock(sessionID, async () => {
|
|
2257
|
-
const current = await read(sessionID);
|
|
2258
|
-
if (!current.ok)
|
|
2259
|
-
return current;
|
|
2260
|
-
const matches = current.value.filter((attachment) => attachment.pullRequest.number === number);
|
|
2261
|
-
if (matches.length === 0)
|
|
2262
|
-
return { ok: true, value: { tag: "absent" } };
|
|
2263
|
-
if (matches.length > 1) {
|
|
2264
|
-
return {
|
|
2265
|
-
ok: true,
|
|
2266
|
-
value: { tag: "ambiguous", pullRequests: matches.map((attachment) => attachment.pullRequest) }
|
|
2267
|
-
};
|
|
2268
|
-
}
|
|
2269
|
-
const match = matches[0];
|
|
2270
|
-
if (match === undefined)
|
|
2271
|
-
return { ok: true, value: { tag: "absent" } };
|
|
2272
|
-
const next = current.value.filter((attachment) => attachment.pullRequest.url !== match.pullRequest.url);
|
|
2273
|
-
const written = await write(sessionID, next);
|
|
2274
|
-
if (!written.ok)
|
|
2275
|
-
return written;
|
|
2276
|
-
return { ok: true, value: { tag: "removed", pullRequest: match.pullRequest } };
|
|
418
|
+
run: (effect) => {
|
|
419
|
+
const fiber = Effect3.runFork(effect);
|
|
420
|
+
running.add(fiber);
|
|
421
|
+
fiber.addObserver(() => {
|
|
422
|
+
running.delete(fiber);
|
|
2277
423
|
});
|
|
2278
424
|
},
|
|
2279
|
-
async
|
|
2280
|
-
|
|
2281
|
-
const current = await readExisting(sessionID);
|
|
2282
|
-
if (!current.ok)
|
|
2283
|
-
return current;
|
|
2284
|
-
if (current.value === undefined)
|
|
2285
|
-
return { ok: true, value: "absent" };
|
|
2286
|
-
try {
|
|
2287
|
-
await rm(join(directory, fileName(sessionID)), { force: true });
|
|
2288
|
-
return { ok: true, value: "removed" };
|
|
2289
|
-
} catch (cause) {
|
|
2290
|
-
return {
|
|
2291
|
-
ok: false,
|
|
2292
|
-
error: stateUnavailable("write", "Unable to remove the session pull request state", cause)
|
|
2293
|
-
};
|
|
2294
|
-
}
|
|
2295
|
-
});
|
|
425
|
+
stop: async () => {
|
|
426
|
+
await Effect3.runPromise(Fiber.interruptAll(running));
|
|
2296
427
|
}
|
|
2297
428
|
};
|
|
2298
429
|
}
|
|
2299
430
|
|
|
2300
|
-
// src/tui.
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
431
|
+
// src/tui/Browser.ts
|
|
432
|
+
import { Effect as Effect4, Option as Option4, Schema as Schema6 } from "effect";
|
|
433
|
+
class OpenFailed extends Schema6.TaggedError()("OpenFailed", {
|
|
434
|
+
message: Schema6.String
|
|
435
|
+
}) {
|
|
436
|
+
}
|
|
437
|
+
var openers = {
|
|
438
|
+
darwin: "open",
|
|
439
|
+
linux: "xdg-open"
|
|
2305
440
|
};
|
|
2306
|
-
function
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
441
|
+
function openUrl(url, platform) {
|
|
442
|
+
return Option4.match(Option4.fromNullishOr(openers[platform]), {
|
|
443
|
+
onNone: () => Effect4.fail(new OpenFailed({ message: `Opening pull requests is not supported on ${platform}.` })),
|
|
444
|
+
onSome: (opener) => CommandRunner.use((runner) => runner.run(opener, [url], ".")).pipe(Effect4.asVoid, Effect4.mapError(() => new OpenFailed({ message: `Could not open ${url} with \`${opener}\`.` })))
|
|
445
|
+
});
|
|
2311
446
|
}
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
return {
|
|
2333
|
-
ok: true,
|
|
2334
|
-
value: "stopped"
|
|
2335
|
-
};
|
|
2336
|
-
if (!attachments.ok) {
|
|
2337
|
-
input.publish([]);
|
|
2338
|
-
input.onStateFailure(attachments.error);
|
|
2339
|
-
return attachments;
|
|
2340
|
-
}
|
|
2341
|
-
const attachedUrls = new Set(attachments.value.map((attachment) => attachment.pullRequest.url));
|
|
2342
|
-
for (const url of statuses.keys()) {
|
|
2343
|
-
if (!attachedUrls.has(url))
|
|
2344
|
-
statuses.delete(url);
|
|
2345
|
-
}
|
|
2346
|
-
input.publish(project(attachments.value));
|
|
2347
|
-
if (attachments.value.length === 0)
|
|
2348
|
-
return {
|
|
2349
|
-
ok: true,
|
|
2350
|
-
value: "no_attachments"
|
|
2351
|
-
};
|
|
2352
|
-
const refreshable = attachments.value.filter((attachment) => {
|
|
2353
|
-
const previous = statuses.get(attachment.pullRequest.url);
|
|
2354
|
-
return previous?.tag !== "Available" || previous.state.tag !== "Merged";
|
|
2355
|
-
});
|
|
2356
|
-
const batch = await input.github.get(refreshable.map((attachment) => attachment.pullRequest), {
|
|
2357
|
-
signal: controller.signal
|
|
2358
|
-
});
|
|
2359
|
-
if (stopped)
|
|
2360
|
-
return {
|
|
2361
|
-
ok: true,
|
|
2362
|
-
value: "stopped"
|
|
2363
|
-
};
|
|
2364
|
-
let batchDiagnostic;
|
|
2365
|
-
let failure;
|
|
2366
|
-
if (!batch.ok) {
|
|
2367
|
-
if (batch.error.tag === "GitHubCancelled")
|
|
2368
|
-
return batch;
|
|
2369
|
-
batchDiagnostic = batch.error.tag === "GitHubBatchLimitExceeded" ? "GitHubUnavailable" : batch.error.tag;
|
|
2370
|
-
failure = batch.error;
|
|
2371
|
-
}
|
|
2372
|
-
for (const [index, attachment] of refreshable.entries()) {
|
|
2373
|
-
const previous = statuses.get(attachment.pullRequest.url);
|
|
2374
|
-
const result = batch.ok ? batch.value[index] : undefined;
|
|
2375
|
-
if (result?.ok) {
|
|
2376
|
-
statuses.set(attachment.pullRequest.url, result.value);
|
|
2377
|
-
continue;
|
|
2378
|
-
}
|
|
2379
|
-
const diagnostic = result === undefined ? batchDiagnostic ?? "GitHubUnavailable" : result.error.tag;
|
|
2380
|
-
if (result !== undefined && !result.ok)
|
|
2381
|
-
failure ??= result.error;
|
|
2382
|
-
statuses.set(attachment.pullRequest.url, previous?.tag === "Available" ? {
|
|
2383
|
-
...previous,
|
|
2384
|
-
stale: true,
|
|
2385
|
-
diagnostic
|
|
2386
|
-
} : {
|
|
2387
|
-
tag: "Unavailable",
|
|
2388
|
-
diagnostic
|
|
2389
|
-
});
|
|
2390
|
-
}
|
|
2391
|
-
if (!stopped)
|
|
2392
|
-
input.publish(project(attachments.value));
|
|
2393
|
-
return failure === undefined ? {
|
|
2394
|
-
ok: true,
|
|
2395
|
-
value: "refreshed"
|
|
2396
|
-
} : {
|
|
2397
|
-
ok: false,
|
|
2398
|
-
error: failure
|
|
2399
|
-
};
|
|
2400
|
-
}
|
|
2401
|
-
function startQueuedRefresh() {
|
|
2402
|
-
inFlight = undefined;
|
|
2403
|
-
const next = queued;
|
|
2404
|
-
queued = undefined;
|
|
2405
|
-
if (next === undefined)
|
|
2406
|
-
return;
|
|
2407
|
-
if (stopped) {
|
|
2408
|
-
next.resolve({
|
|
2409
|
-
ok: true,
|
|
2410
|
-
value: "stopped"
|
|
2411
|
-
});
|
|
2412
|
-
return;
|
|
2413
|
-
}
|
|
2414
|
-
requestRefresh().then((result) => next.resolve(result), (error) => next.reject(error));
|
|
2415
|
-
}
|
|
2416
|
-
function requestRefresh() {
|
|
2417
|
-
if (stopped)
|
|
2418
|
-
return Promise.resolve({
|
|
2419
|
-
ok: true,
|
|
2420
|
-
value: "stopped"
|
|
2421
|
-
});
|
|
2422
|
-
if (inFlight) {
|
|
2423
|
-
if (queued !== undefined)
|
|
2424
|
-
return queued.promise;
|
|
2425
|
-
let resolve;
|
|
2426
|
-
let reject;
|
|
2427
|
-
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
2428
|
-
resolve = resolvePromise;
|
|
2429
|
-
reject = rejectPromise;
|
|
2430
|
-
});
|
|
2431
|
-
queued = {
|
|
2432
|
-
promise,
|
|
2433
|
-
resolve,
|
|
2434
|
-
reject
|
|
2435
|
-
};
|
|
2436
|
-
return promise;
|
|
447
|
+
|
|
448
|
+
// src/tui/Client.ts
|
|
449
|
+
import { Effect as Effect5, Option as Option5, Schema as Schema7 } from "effect";
|
|
450
|
+
class RequestFailed extends Schema7.TaggedError()("RequestFailed", {
|
|
451
|
+
message: Schema7.String
|
|
452
|
+
}) {
|
|
453
|
+
}
|
|
454
|
+
var Thrown = Schema7.Union([
|
|
455
|
+
Schema7.Struct({
|
|
456
|
+
data: Schema7.Struct({ message: Schema7.String }),
|
|
457
|
+
type: Schema7.Literal("rejected")
|
|
458
|
+
}),
|
|
459
|
+
Schema7.Struct({ message: Schema7.String, type: Schema7.String })
|
|
460
|
+
]);
|
|
461
|
+
function failureOf(thrown) {
|
|
462
|
+
const message = Option5.match(thrown, {
|
|
463
|
+
onNone: () => "The pull request tracker failed.",
|
|
464
|
+
onSome: (failure) => {
|
|
465
|
+
if ("data" in failure)
|
|
466
|
+
return failure.data.message;
|
|
467
|
+
return failure.type === "rpc.unavailable" ? "The pull request tracker is not running for this session's directory." : `The pull request tracker failed: ${failure.message}`;
|
|
2437
468
|
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
469
|
+
});
|
|
470
|
+
return new RequestFailed({ message });
|
|
471
|
+
}
|
|
472
|
+
var unreadable = new RequestFailed({
|
|
473
|
+
message: "The pull request tracker sent a response the terminal could not read."
|
|
474
|
+
});
|
|
475
|
+
function decoded(schema, call) {
|
|
476
|
+
return Effect5.tryPromise({
|
|
477
|
+
catch: (error) => failureOf(Schema7.decodeUnknownOption(Thrown)(error)),
|
|
478
|
+
try: call
|
|
479
|
+
}).pipe(Effect5.flatMap((output) => Schema7.decodeEffect(schema)(output).pipe(Effect5.mapError(() => unreadable))));
|
|
480
|
+
}
|
|
481
|
+
function makeClient(host) {
|
|
482
|
+
const { rpc } = host;
|
|
483
|
+
const options = (sessionID) => Option5.match(host.locationOf(sessionID), {
|
|
484
|
+
onNone: () => ({}),
|
|
485
|
+
onSome: (location) => ({ location })
|
|
486
|
+
});
|
|
2448
487
|
return {
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
488
|
+
attach: (sessionID, target) => decoded(Changed, async () => {
|
|
489
|
+
const output = await rpc.attach({ sessionID, target }, options(sessionID));
|
|
490
|
+
return output;
|
|
491
|
+
}),
|
|
492
|
+
detach: (sessionID, target) => decoded(Changed, async () => {
|
|
493
|
+
const output = await rpc.detach({ sessionID, target }, options(sessionID));
|
|
494
|
+
return output;
|
|
495
|
+
}),
|
|
496
|
+
list: (sessionID) => decoded(View, async () => {
|
|
497
|
+
const output = await rpc.list({ sessionID }, options(sessionID));
|
|
498
|
+
return output;
|
|
499
|
+
}),
|
|
500
|
+
onUpdate: (handler) => rpc.events.on("updated", ({ data }) => {
|
|
501
|
+
Option5.map(Schema7.decodeUnknownOption(View)(data), handler);
|
|
502
|
+
}),
|
|
503
|
+
refresh: (sessionID) => decoded(View, async () => {
|
|
504
|
+
const output = await rpc.refresh({ sessionID }, options(sessionID));
|
|
505
|
+
return output;
|
|
506
|
+
})
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/tui/Commands.tsx
|
|
511
|
+
import { usePlugin } from "@opencode/plugin/tui";
|
|
512
|
+
import { Option as Option6 } from "effect";
|
|
513
|
+
var definitions = (actions) => [{
|
|
514
|
+
act: actions.attach,
|
|
515
|
+
name: "attach",
|
|
516
|
+
takesInput: true,
|
|
517
|
+
title: "Attach pull request"
|
|
518
|
+
}, {
|
|
519
|
+
act: () => actions.open,
|
|
520
|
+
name: "open",
|
|
521
|
+
takesInput: false,
|
|
522
|
+
title: "Open pull request"
|
|
523
|
+
}, {
|
|
524
|
+
act: () => actions.detach,
|
|
525
|
+
name: "detach",
|
|
526
|
+
takesInput: false,
|
|
527
|
+
title: "Detach pull request"
|
|
528
|
+
}, {
|
|
529
|
+
act: () => actions.sync,
|
|
530
|
+
name: "sync",
|
|
531
|
+
takesInput: false,
|
|
532
|
+
title: "Sync pull request status"
|
|
533
|
+
}];
|
|
534
|
+
function command(definition, run) {
|
|
535
|
+
const slash = definition.takesInput ? {
|
|
536
|
+
arguments: true,
|
|
537
|
+
name: `pr-${definition.name}`
|
|
538
|
+
} : {
|
|
539
|
+
name: `pr-${definition.name}`
|
|
540
|
+
};
|
|
541
|
+
return {
|
|
542
|
+
group: "Pull requests",
|
|
543
|
+
id: `pr.${definition.name}`,
|
|
544
|
+
palette: true,
|
|
545
|
+
run: (input) => {
|
|
546
|
+
run(definition.act(Option6.fromNullishOr(input)));
|
|
2459
547
|
},
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
stop() {
|
|
2463
|
-
if (stopped)
|
|
2464
|
-
return;
|
|
2465
|
-
stopped = true;
|
|
2466
|
-
controller.abort();
|
|
2467
|
-
if (timerRegistered)
|
|
2468
|
-
scheduler.clearInterval(timer);
|
|
2469
|
-
}
|
|
548
|
+
slash,
|
|
549
|
+
title: definition.title
|
|
2470
550
|
};
|
|
2471
551
|
}
|
|
2472
|
-
|
|
2473
|
-
const
|
|
2474
|
-
const
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
message: `Opening pull requests is unsupported on ${platform}`,
|
|
2481
|
-
platform
|
|
2482
|
-
}
|
|
2483
|
-
};
|
|
2484
|
-
}
|
|
2485
|
-
try {
|
|
2486
|
-
await (options.runner ?? execFileRunner)(executable, [pullRequest.url], options.signal ? {
|
|
2487
|
-
signal: options.signal
|
|
2488
|
-
} : {});
|
|
2489
|
-
return {
|
|
2490
|
-
ok: true,
|
|
2491
|
-
value: undefined
|
|
2492
|
-
};
|
|
2493
|
-
} catch (cause) {
|
|
2494
|
-
return {
|
|
2495
|
-
ok: false,
|
|
2496
|
-
error: {
|
|
2497
|
-
tag: "OpenPullRequestFailed",
|
|
2498
|
-
message: "Unable to open the pull request",
|
|
2499
|
-
cause
|
|
2500
|
-
}
|
|
2501
|
-
};
|
|
2502
|
-
}
|
|
552
|
+
function Commands(props) {
|
|
553
|
+
const context = usePlugin();
|
|
554
|
+
const commands = definitions(props.actions).map((definition) => command(definition, props.run));
|
|
555
|
+
context.keymap.layer(() => ({
|
|
556
|
+
commands,
|
|
557
|
+
mode: "global"
|
|
558
|
+
}));
|
|
559
|
+
return null;
|
|
2503
560
|
}
|
|
2504
|
-
|
|
2505
|
-
|
|
561
|
+
|
|
562
|
+
// src/tui/SessionSidebar.tsx
|
|
563
|
+
import { createComponent as _$createComponent3 } from "@opentui/solid";
|
|
564
|
+
import { usePlugin as usePlugin2 } from "@opencode/plugin/tui";
|
|
565
|
+
import { Effect as Effect6 } from "effect";
|
|
566
|
+
import { createEffect, createSignal as createSignal2, on, onCleanup } from "solid-js";
|
|
567
|
+
|
|
568
|
+
// src/ui/Palette.ts
|
|
569
|
+
import { RGBA } from "@opentui/core";
|
|
570
|
+
var merged = {
|
|
571
|
+
dark: RGBA.fromHex("#a371f7"),
|
|
572
|
+
light: RGBA.fromHex("#8250df")
|
|
573
|
+
};
|
|
574
|
+
function paletteOf(theme, mode) {
|
|
2506
575
|
return {
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
const settled = await Promise.allSettled([...sessionListeners].map((listener) => listener.forceRefresh()));
|
|
2516
|
-
const rejected = settled.find((result) => result.status === "rejected");
|
|
2517
|
-
if (rejected !== undefined)
|
|
2518
|
-
throw rejected.reason;
|
|
2519
|
-
const results = settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2520
|
-
return results.find((result) => !result.ok) ?? results.find((result) => result.ok && result.value === "refreshed") ?? results[0];
|
|
2521
|
-
},
|
|
2522
|
-
subscribe(sessionID, listener) {
|
|
2523
|
-
const sessionListeners = listeners.get(sessionID) ?? new Set;
|
|
2524
|
-
sessionListeners.add(listener);
|
|
2525
|
-
listeners.set(sessionID, sessionListeners);
|
|
2526
|
-
return () => {
|
|
2527
|
-
sessionListeners.delete(listener);
|
|
2528
|
-
if (sessionListeners.size === 0)
|
|
2529
|
-
listeners.delete(sessionID);
|
|
2530
|
-
};
|
|
576
|
+
muted: theme.text.muted,
|
|
577
|
+
text: theme.text.base,
|
|
578
|
+
tones: {
|
|
579
|
+
gray: theme.text.muted,
|
|
580
|
+
green: theme.text.feedback.success.base,
|
|
581
|
+
purple: merged[mode],
|
|
582
|
+
red: theme.text.feedback.error.base,
|
|
583
|
+
yellow: theme.text.feedback.warning.base
|
|
2531
584
|
}
|
|
2532
585
|
};
|
|
2533
586
|
}
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
587
|
+
|
|
588
|
+
// src/ui/Sidebar.tsx
|
|
589
|
+
import { createTextNode as _$createTextNode2 } from "@opentui/solid";
|
|
590
|
+
import { insertNode as _$insertNode2 } from "@opentui/solid";
|
|
591
|
+
import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
592
|
+
import { effect as _$effect2 } from "@opentui/solid";
|
|
593
|
+
import { insert as _$insert2 } from "@opentui/solid";
|
|
594
|
+
import { memo as _$memo } from "@opentui/solid";
|
|
595
|
+
import { setProp as _$setProp2 } from "@opentui/solid";
|
|
596
|
+
import { createElement as _$createElement2 } from "@opentui/solid";
|
|
597
|
+
import { Match as Match4, Option as Option8 } from "effect";
|
|
598
|
+
import { For as For2, Show as Show2 } from "solid-js";
|
|
599
|
+
|
|
600
|
+
// src/ui/Row.tsx
|
|
601
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
602
|
+
import { use as _$use } from "@opentui/solid";
|
|
603
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
604
|
+
import { setProp as _$setProp } from "@opentui/solid";
|
|
605
|
+
import { effect as _$effect } from "@opentui/solid";
|
|
606
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
607
|
+
import { insert as _$insert } from "@opentui/solid";
|
|
608
|
+
import { createElement as _$createElement } from "@opentui/solid";
|
|
609
|
+
import { Match as Match3, Option as Option7 } from "effect";
|
|
610
|
+
import { createSignal, For, Show } from "solid-js";
|
|
611
|
+
|
|
612
|
+
// src/domain/Appearance.ts
|
|
613
|
+
import { Match as Match2 } from "effect";
|
|
614
|
+
var diagnosticLabels = {
|
|
615
|
+
AuthenticationRequired: "authenticate",
|
|
616
|
+
GitHubCliMissing: "install gh",
|
|
617
|
+
GitHubUnavailable: "GitHub unavailable",
|
|
618
|
+
InvalidResponse: "invalid response",
|
|
619
|
+
NotFound: "inaccessible"
|
|
620
|
+
};
|
|
621
|
+
var shown = (tone, label, strikethrough = false) => ({
|
|
622
|
+
label,
|
|
623
|
+
stale: false,
|
|
624
|
+
strikethrough,
|
|
625
|
+
tone
|
|
626
|
+
});
|
|
627
|
+
function openAppearance(open) {
|
|
628
|
+
if (open.mergeability === "conflicting")
|
|
629
|
+
return shown("red", "conflict");
|
|
630
|
+
if (open.ci === "failed")
|
|
631
|
+
return shown("red", "failed");
|
|
632
|
+
if (open.draft)
|
|
633
|
+
return shown("gray", "draft");
|
|
634
|
+
if (open.ci === "pending")
|
|
635
|
+
return shown("yellow", "pending");
|
|
636
|
+
if (open.behind)
|
|
637
|
+
return shown("yellow", "behind");
|
|
638
|
+
return open.ci === "passed" ? shown("green", "passed") : shown("gray", "no checks");
|
|
2539
639
|
}
|
|
2540
|
-
function
|
|
2541
|
-
return
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
const finish = (value) => {
|
|
2546
|
-
if (finished)
|
|
2547
|
-
return;
|
|
2548
|
-
finished = true;
|
|
2549
|
-
controller.abort();
|
|
2550
|
-
api.ui.dialog.clear();
|
|
2551
|
-
resolve(value);
|
|
2552
|
-
};
|
|
2553
|
-
api.ui.dialog.setSize("medium");
|
|
2554
|
-
api.ui.dialog.replace(() => {
|
|
2555
|
-
const [error, setError] = createSignal();
|
|
2556
|
-
const [busy, setBusy] = createSignal(false);
|
|
2557
|
-
const DialogPrompt = api.ui.DialogPrompt;
|
|
2558
|
-
return _$createComponent(DialogPrompt, {
|
|
2559
|
-
title: "Attach pull request",
|
|
2560
|
-
placeholder: "https://github.com/owner/repository/pull/123 or 123",
|
|
2561
|
-
description: () => error() ? (() => {
|
|
2562
|
-
var _el$ = _$createElement("text");
|
|
2563
|
-
_$insert(_el$, error);
|
|
2564
|
-
_$effect((_$p) => _$setProp(_el$, "fg", api.theme.current.error, _$p));
|
|
2565
|
-
return _el$;
|
|
2566
|
-
})() : null,
|
|
2567
|
-
get busy() {
|
|
2568
|
-
return busy();
|
|
2569
|
-
},
|
|
2570
|
-
busyText: "Resolving repository",
|
|
2571
|
-
onConfirm: (value) => {
|
|
2572
|
-
if (busy())
|
|
2573
|
-
return;
|
|
2574
|
-
setBusy(true);
|
|
2575
|
-
resolvePullRequestInput(value, {
|
|
2576
|
-
directory: options.directory,
|
|
2577
|
-
...options.runner ? {
|
|
2578
|
-
runner: options.runner
|
|
2579
|
-
} : {},
|
|
2580
|
-
signal
|
|
2581
|
-
}).then((result) => {
|
|
2582
|
-
if (finished)
|
|
2583
|
-
return;
|
|
2584
|
-
setBusy(false);
|
|
2585
|
-
if (result.ok) {
|
|
2586
|
-
finish(result.value);
|
|
2587
|
-
return;
|
|
2588
|
-
}
|
|
2589
|
-
if (result.error.tag === "RepositoryResolutionCancelled") {
|
|
2590
|
-
finish(undefined);
|
|
2591
|
-
return;
|
|
2592
|
-
}
|
|
2593
|
-
setError(result.error.message);
|
|
2594
|
-
});
|
|
2595
|
-
},
|
|
2596
|
-
onCancel: () => finish(undefined)
|
|
2597
|
-
});
|
|
2598
|
-
}, () => {
|
|
2599
|
-
if (finished)
|
|
2600
|
-
return;
|
|
2601
|
-
finished = true;
|
|
2602
|
-
controller.abort();
|
|
2603
|
-
resolve(undefined);
|
|
2604
|
-
});
|
|
640
|
+
function stateAppearance(state) {
|
|
641
|
+
return Match2.valueTags(state, {
|
|
642
|
+
Closed: () => shown("red", "closed", true),
|
|
643
|
+
Merged: () => shown("purple", "merged", true),
|
|
644
|
+
Open: openAppearance
|
|
2605
645
|
});
|
|
2606
646
|
}
|
|
2607
|
-
function
|
|
2608
|
-
return
|
|
2609
|
-
let finished = false;
|
|
2610
|
-
const finish = (value) => {
|
|
2611
|
-
if (finished)
|
|
2612
|
-
return;
|
|
2613
|
-
finished = true;
|
|
2614
|
-
api.ui.dialog.clear();
|
|
2615
|
-
resolve(value);
|
|
2616
|
-
};
|
|
2617
|
-
api.ui.dialog.setSize("medium");
|
|
2618
|
-
api.ui.dialog.replace(() => {
|
|
2619
|
-
const DialogSelect = api.ui.DialogSelect;
|
|
2620
|
-
return _$createComponent(DialogSelect, {
|
|
2621
|
-
title,
|
|
2622
|
-
get options() {
|
|
2623
|
-
return attachments.map((attachment) => ({
|
|
2624
|
-
title: formatPullRequestRef(attachment.pullRequest),
|
|
2625
|
-
value: attachment.pullRequest,
|
|
2626
|
-
description: attachment.pullRequest.url
|
|
2627
|
-
}));
|
|
2628
|
-
},
|
|
2629
|
-
onSelect: (option) => finish(option.value)
|
|
2630
|
-
});
|
|
2631
|
-
}, () => {
|
|
2632
|
-
if (!finished)
|
|
2633
|
-
resolve(undefined);
|
|
2634
|
-
});
|
|
2635
|
-
});
|
|
647
|
+
function markStale(fresh) {
|
|
648
|
+
return { label: fresh.label, stale: true, strikethrough: fresh.strikethrough, tone: fresh.tone };
|
|
2636
649
|
}
|
|
2637
|
-
function
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
650
|
+
function appearance(status) {
|
|
651
|
+
return Match2.valueTags(status, {
|
|
652
|
+
Fresh: ({ snapshot }) => stateAppearance(snapshot.state),
|
|
653
|
+
Pending: () => shown("gray", "unavailable"),
|
|
654
|
+
Stale: ({ snapshot }) => markStale(stateAppearance(snapshot.state)),
|
|
655
|
+
Unavailable: ({ diagnostic }) => shown("gray", diagnosticLabels[diagnostic])
|
|
2642
656
|
});
|
|
2643
657
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
658
|
+
|
|
659
|
+
// src/ui/Row.tsx
|
|
660
|
+
var markers = {
|
|
661
|
+
bullet: {
|
|
662
|
+
line: "",
|
|
663
|
+
tick: "\u2022"
|
|
664
|
+
},
|
|
665
|
+
first: {
|
|
666
|
+
line: "\u250C",
|
|
667
|
+
tick: "\u2500"
|
|
668
|
+
},
|
|
669
|
+
last: {
|
|
670
|
+
line: "\u2514",
|
|
671
|
+
tick: "\u2500"
|
|
672
|
+
},
|
|
673
|
+
middle: {
|
|
674
|
+
line: "\u251C",
|
|
675
|
+
tick: "\u2500"
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
var continuations = {
|
|
679
|
+
continues: {
|
|
680
|
+
line: "\u2502",
|
|
681
|
+
tick: ""
|
|
682
|
+
},
|
|
683
|
+
none: {
|
|
684
|
+
line: "",
|
|
685
|
+
tick: ""
|
|
686
|
+
},
|
|
687
|
+
open: {
|
|
688
|
+
line: "\u250A",
|
|
689
|
+
tick: ""
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
var markerWidth = 3;
|
|
693
|
+
function MarkerGlyph(props) {
|
|
694
|
+
const padding = () => " ".repeat(markerWidth - props.glyph.line.length - props.glyph.tick.length);
|
|
695
|
+
return (() => {
|
|
696
|
+
var _el$ = _$createElement("text"), _el$2 = _$createElement("span"), _el$3 = _$createElement("span");
|
|
697
|
+
_$insertNode(_el$, _el$2);
|
|
698
|
+
_$insertNode(_el$, _el$3);
|
|
699
|
+
_$insert(_el$2, () => props.glyph.line);
|
|
700
|
+
_$insert(_el$3, () => props.glyph.tick);
|
|
701
|
+
_$insert(_el$, padding, null);
|
|
702
|
+
_$effect((_p$) => {
|
|
703
|
+
var _v$ = {
|
|
704
|
+
fg: props.palette.muted
|
|
705
|
+
}, _v$2 = {
|
|
706
|
+
fg: props.color
|
|
707
|
+
};
|
|
708
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$2, "style", _v$, _p$.e));
|
|
709
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$3, "style", _v$2, _p$.t));
|
|
710
|
+
return _p$;
|
|
711
|
+
}, {
|
|
712
|
+
e: undefined,
|
|
713
|
+
t: undefined
|
|
714
|
+
});
|
|
715
|
+
return _el$;
|
|
716
|
+
})();
|
|
2653
717
|
}
|
|
2654
|
-
function
|
|
2655
|
-
const [
|
|
2656
|
-
|
|
2657
|
-
const
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
},
|
|
2665
|
-
onStateFailure: (error) => setFailure(error.message),
|
|
2666
|
-
onError: () => setFailure("Unable to refresh pull request status")
|
|
2667
|
-
});
|
|
2668
|
-
polling.start().catch(() => setFailure("Unable to refresh pull request status"));
|
|
2669
|
-
const unsubscribe = props.refreshBus.subscribe(props.sessionID, {
|
|
2670
|
-
refresh() {
|
|
2671
|
-
polling.refresh().catch(() => setFailure("Unable to refresh pull request status"));
|
|
2672
|
-
},
|
|
2673
|
-
async forceRefresh() {
|
|
2674
|
-
try {
|
|
2675
|
-
return await polling.forceRefresh();
|
|
2676
|
-
} catch (error) {
|
|
2677
|
-
setFailure("Unable to refresh pull request status");
|
|
2678
|
-
throw error;
|
|
2679
|
-
}
|
|
2680
|
-
}
|
|
2681
|
-
});
|
|
2682
|
-
const onAbort = () => polling.stop();
|
|
2683
|
-
props.api.lifecycle.signal.addEventListener("abort", onAbort, {
|
|
2684
|
-
once: true
|
|
2685
|
-
});
|
|
2686
|
-
onCleanup(() => {
|
|
2687
|
-
unsubscribe();
|
|
2688
|
-
polling.stop();
|
|
2689
|
-
props.api.lifecycle.signal.removeEventListener("abort", onAbort);
|
|
2690
|
-
});
|
|
718
|
+
function Marked(props) {
|
|
719
|
+
const [lines, setLines] = createSignal(1);
|
|
720
|
+
let content = Option7.none();
|
|
721
|
+
const measure = () => {
|
|
722
|
+
const count = Option7.match(content, {
|
|
723
|
+
onNone: () => 1,
|
|
724
|
+
onSome: (text) => text.virtualLineCount
|
|
725
|
+
});
|
|
726
|
+
setLines(Math.max(1, count));
|
|
727
|
+
};
|
|
2691
728
|
return (() => {
|
|
2692
|
-
var _el$
|
|
2693
|
-
_$insertNode(_el$
|
|
2694
|
-
_$
|
|
2695
|
-
_$setProp(_el$
|
|
2696
|
-
_$
|
|
2697
|
-
_$
|
|
2698
|
-
_$
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
return
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
})() : null;
|
|
2715
|
-
})(), null);
|
|
2716
|
-
_$insert(_el$2, () => items().map((item) => {
|
|
2717
|
-
const appearance = statusAppearance(item.status);
|
|
2718
|
-
const attributes = appearance.strikethrough ? TextAttributes.STRIKETHROUGH : TextAttributes.NONE;
|
|
2719
|
-
const title = item.status.tag === "Available" ? item.status.title : "Title unavailable";
|
|
2720
|
-
return (() => {
|
|
2721
|
-
var _el$9 = _$createElement("box"), _el$0 = _$createElement("text"), _el$1 = _$createElement("b"), _el$10 = _$createTextNode(` `), _el$11 = _$createElement("text");
|
|
2722
|
-
_$insertNode(_el$9, _el$0);
|
|
2723
|
-
_$insertNode(_el$9, _el$11);
|
|
2724
|
-
_$setProp(_el$9, "flexDirection", "column");
|
|
2725
|
-
_$setProp(_el$9, "onMouseUp", () => {
|
|
2726
|
-
openPullRequest(item.attachment.pullRequest, {
|
|
2727
|
-
...props.dependencies.runner ? {
|
|
2728
|
-
runner: props.dependencies.runner
|
|
2729
|
-
} : {},
|
|
2730
|
-
signal: props.api.lifecycle.signal
|
|
2731
|
-
}).then((result) => {
|
|
2732
|
-
if (!result.ok) {
|
|
2733
|
-
props.api.ui.toast({
|
|
2734
|
-
variant: "error",
|
|
2735
|
-
title: "Pull request tracker",
|
|
2736
|
-
message: result.error.message
|
|
2737
|
-
});
|
|
2738
|
-
}
|
|
2739
|
-
}).catch(() => {
|
|
2740
|
-
props.api.ui.toast({
|
|
2741
|
-
variant: "error",
|
|
2742
|
-
title: "Pull request tracker",
|
|
2743
|
-
message: "Unable to open the pull request"
|
|
2744
|
-
});
|
|
2745
|
-
});
|
|
2746
|
-
});
|
|
2747
|
-
_$insertNode(_el$0, _el$1);
|
|
2748
|
-
_$insertNode(_el$0, _el$10);
|
|
2749
|
-
_$setProp(_el$0, "attributes", attributes);
|
|
2750
|
-
_$insert(_el$1, () => formatPullRequestRef(item.attachment.pullRequest));
|
|
2751
|
-
_$insert(_el$0, () => appearance.label, null);
|
|
2752
|
-
_$setProp(_el$11, "attributes", attributes);
|
|
2753
|
-
_$insert(_el$11, title);
|
|
2754
|
-
_$effect((_p$) => {
|
|
2755
|
-
var _v$ = toneColor(props.api.theme.current, appearance.tone), _v$2 = props.api.theme.current.textMuted;
|
|
2756
|
-
_v$ !== _p$.e && (_p$.e = _$setProp(_el$0, "fg", _v$, _p$.e));
|
|
2757
|
-
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$11, "fg", _v$2, _p$.t));
|
|
2758
|
-
return _p$;
|
|
2759
|
-
}, {
|
|
2760
|
-
e: undefined,
|
|
2761
|
-
t: undefined
|
|
729
|
+
var _el$4 = _$createElement("box"), _el$5 = _$createElement("box"), _el$6 = _$createElement("text");
|
|
730
|
+
_$insertNode(_el$4, _el$5);
|
|
731
|
+
_$insertNode(_el$4, _el$6);
|
|
732
|
+
_$setProp(_el$4, "flexDirection", "row");
|
|
733
|
+
_$setProp(_el$4, "width", "100%");
|
|
734
|
+
_$setProp(_el$5, "flexDirection", "column");
|
|
735
|
+
_$setProp(_el$5, "width", 3);
|
|
736
|
+
_$insert(_el$5, _$createComponent(MarkerGlyph, {
|
|
737
|
+
get color() {
|
|
738
|
+
return props.color;
|
|
739
|
+
},
|
|
740
|
+
get glyph() {
|
|
741
|
+
return props.first;
|
|
742
|
+
},
|
|
743
|
+
get palette() {
|
|
744
|
+
return props.palette;
|
|
745
|
+
}
|
|
746
|
+
}), null);
|
|
747
|
+
_$insert(_el$5, _$createComponent(For, {
|
|
748
|
+
get each() {
|
|
749
|
+
return Array.from({
|
|
750
|
+
length: lines() - 1
|
|
2762
751
|
});
|
|
2763
|
-
|
|
2764
|
-
|
|
752
|
+
},
|
|
753
|
+
children: () => _$createComponent(MarkerGlyph, {
|
|
754
|
+
get color() {
|
|
755
|
+
return props.color;
|
|
756
|
+
},
|
|
757
|
+
get glyph() {
|
|
758
|
+
return props.rest;
|
|
759
|
+
},
|
|
760
|
+
get palette() {
|
|
761
|
+
return props.palette;
|
|
762
|
+
}
|
|
763
|
+
})
|
|
2765
764
|
}), null);
|
|
2766
|
-
_$
|
|
2767
|
-
|
|
765
|
+
_$use((text) => {
|
|
766
|
+
content = Option7.some(text);
|
|
767
|
+
measure();
|
|
768
|
+
}, _el$6);
|
|
769
|
+
_$setProp(_el$6, "flexGrow", 1);
|
|
770
|
+
_$setProp(_el$6, "on:line-info-change", measure);
|
|
771
|
+
_$insert(_el$6, () => props.children);
|
|
772
|
+
return _el$4;
|
|
2768
773
|
})();
|
|
2769
774
|
}
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
775
|
+
var titleOf = (status) => Match3.valueTags(status, {
|
|
776
|
+
Fresh: ({
|
|
777
|
+
snapshot
|
|
778
|
+
}) => snapshot.title,
|
|
779
|
+
Pending: () => "Title unavailable",
|
|
780
|
+
Stale: ({
|
|
781
|
+
snapshot
|
|
782
|
+
}) => snapshot.title,
|
|
783
|
+
Unavailable: () => "Title unavailable"
|
|
784
|
+
});
|
|
785
|
+
function PullRequestRow(props) {
|
|
786
|
+
const shown = () => appearance(props.entry.status);
|
|
787
|
+
const color = () => props.palette.tones[shown().tone];
|
|
788
|
+
const below = () => continuations[props.connector];
|
|
789
|
+
return (() => {
|
|
790
|
+
var _el$7 = _$createElement("box");
|
|
791
|
+
_$setProp(_el$7, "flexDirection", "column");
|
|
792
|
+
_$setProp(_el$7, "onMouseUp", () => {
|
|
793
|
+
props.onOpen(props.entry.ref);
|
|
794
|
+
});
|
|
795
|
+
_$insert(_el$7, _$createComponent(Marked, {
|
|
796
|
+
get color() {
|
|
797
|
+
return color();
|
|
798
|
+
},
|
|
799
|
+
get first() {
|
|
800
|
+
return markers[props.marker];
|
|
801
|
+
},
|
|
802
|
+
get palette() {
|
|
803
|
+
return props.palette;
|
|
804
|
+
},
|
|
805
|
+
get rest() {
|
|
806
|
+
return below();
|
|
807
|
+
},
|
|
808
|
+
get children() {
|
|
809
|
+
return [(() => {
|
|
810
|
+
var _el$8 = _$createElement("span");
|
|
811
|
+
_$insert(_el$8, () => props.entry.ref.label);
|
|
812
|
+
_$effect((_$p) => _$setProp(_el$8, "style", {
|
|
813
|
+
bold: true,
|
|
814
|
+
fg: color(),
|
|
815
|
+
strikethrough: shown().strikethrough
|
|
816
|
+
}, _$p));
|
|
817
|
+
return _el$8;
|
|
818
|
+
})(), (() => {
|
|
819
|
+
var _el$9 = _$createElement("span");
|
|
820
|
+
_$insert(_el$9, () => ` ${shown().label}`);
|
|
821
|
+
_$effect((_$p) => _$setProp(_el$9, "style", {
|
|
822
|
+
fg: color()
|
|
823
|
+
}, _$p));
|
|
824
|
+
return _el$9;
|
|
825
|
+
})(), _$createComponent(Show, {
|
|
826
|
+
get when() {
|
|
827
|
+
return shown().stale;
|
|
828
|
+
},
|
|
829
|
+
get children() {
|
|
830
|
+
var _el$0 = _$createElement("span");
|
|
831
|
+
_$insertNode(_el$0, _$createTextNode(` \xB7 stale`));
|
|
832
|
+
_$effect((_$p) => _$setProp(_el$0, "style", {
|
|
833
|
+
fg: props.palette.muted,
|
|
834
|
+
italic: true
|
|
835
|
+
}, _$p));
|
|
836
|
+
return _el$0;
|
|
837
|
+
}
|
|
838
|
+
})];
|
|
2811
839
|
}
|
|
2812
|
-
},
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
}
|
|
2841
|
-
const pullRequest = await selectPullRequest(api, "Open pull request", attachments.value);
|
|
2842
|
-
if (pullRequest === undefined)
|
|
2843
|
-
return;
|
|
2844
|
-
const result = await openPullRequest(pullRequest, {
|
|
2845
|
-
...dependencies.runner ? {
|
|
2846
|
-
runner: dependencies.runner
|
|
2847
|
-
} : {},
|
|
2848
|
-
signal: api.lifecycle.signal
|
|
840
|
+
}), null);
|
|
841
|
+
_$insert(_el$7, _$createComponent(Show, {
|
|
842
|
+
get when() {
|
|
843
|
+
return !props.compact;
|
|
844
|
+
},
|
|
845
|
+
get children() {
|
|
846
|
+
return _$createComponent(Marked, {
|
|
847
|
+
get color() {
|
|
848
|
+
return color();
|
|
849
|
+
},
|
|
850
|
+
get first() {
|
|
851
|
+
return below();
|
|
852
|
+
},
|
|
853
|
+
get palette() {
|
|
854
|
+
return props.palette;
|
|
855
|
+
},
|
|
856
|
+
get rest() {
|
|
857
|
+
return below();
|
|
858
|
+
},
|
|
859
|
+
get children() {
|
|
860
|
+
var _el$10 = _$createElement("span");
|
|
861
|
+
_$insert(_el$10, () => titleOf(props.entry.status));
|
|
862
|
+
_$effect((_$p) => _$setProp(_el$10, "style", {
|
|
863
|
+
fg: props.palette.muted,
|
|
864
|
+
strikethrough: shown().strikethrough
|
|
865
|
+
}, _$p));
|
|
866
|
+
return _el$10;
|
|
867
|
+
}
|
|
2849
868
|
});
|
|
2850
|
-
if (!result.ok) {
|
|
2851
|
-
api.ui.toast({
|
|
2852
|
-
variant: "error",
|
|
2853
|
-
title: "Pull request tracker",
|
|
2854
|
-
message: result.error.message
|
|
2855
|
-
});
|
|
2856
|
-
}
|
|
2857
869
|
}
|
|
2858
|
-
},
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
if (attachments.value.length === 0) {
|
|
2880
|
-
api.ui.toast({
|
|
2881
|
-
variant: "info",
|
|
2882
|
-
title: "Pull request tracker",
|
|
2883
|
-
message: "No pull requests are attached"
|
|
2884
|
-
});
|
|
2885
|
-
return;
|
|
2886
|
-
}
|
|
2887
|
-
const pullRequest = await selectPullRequest(api, "Detach pull request", attachments.value);
|
|
2888
|
-
if (pullRequest === undefined)
|
|
2889
|
-
return;
|
|
2890
|
-
const result = await dependencies.store.detach(sessionID, pullRequest);
|
|
2891
|
-
if (!result.ok) {
|
|
2892
|
-
showStateFailure(api, result.error);
|
|
2893
|
-
return;
|
|
2894
|
-
}
|
|
2895
|
-
const message = result.value === "removed" ? `Detached ${formatPullRequestRef(pullRequest)}` : `${formatPullRequestRef(pullRequest)} was not attached`;
|
|
2896
|
-
api.ui.toast({
|
|
2897
|
-
variant: "success",
|
|
2898
|
-
title: "Pull request tracker",
|
|
2899
|
-
message
|
|
2900
|
-
});
|
|
2901
|
-
refreshBus.emit(sessionID);
|
|
870
|
+
}), null);
|
|
871
|
+
return _el$7;
|
|
872
|
+
})();
|
|
873
|
+
}
|
|
874
|
+
function GapRow(props) {
|
|
875
|
+
const label = () => `${String(props.count)} ${props.count === 1 ? "PR" : "PRs"} not attached`;
|
|
876
|
+
return (() => {
|
|
877
|
+
var _el$11 = _$createElement("text");
|
|
878
|
+
_$insert(_el$11, () => `\u251C\u2504 ${label()}`);
|
|
879
|
+
_$effect((_$p) => _$setProp(_el$11, "fg", props.palette.muted, _$p));
|
|
880
|
+
return _el$11;
|
|
881
|
+
})();
|
|
882
|
+
}
|
|
883
|
+
function SidebarRow(props) {
|
|
884
|
+
return Match3.valueTags(props.row, {
|
|
885
|
+
Gap: ({
|
|
886
|
+
count
|
|
887
|
+
}) => _$createComponent(GapRow, {
|
|
888
|
+
count,
|
|
889
|
+
get palette() {
|
|
890
|
+
return props.palette;
|
|
2902
891
|
}
|
|
2903
|
-
},
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
const result = await refreshBus.forceRefresh(sessionID);
|
|
2921
|
-
if (result === undefined) {
|
|
2922
|
-
api.ui.toast({
|
|
2923
|
-
variant: "warning",
|
|
2924
|
-
title: "Pull request tracker",
|
|
2925
|
-
message: "Pull request sidebar is not available"
|
|
2926
|
-
});
|
|
2927
|
-
return;
|
|
2928
|
-
}
|
|
2929
|
-
if (!result.ok) {
|
|
2930
|
-
api.ui.toast({
|
|
2931
|
-
variant: "error",
|
|
2932
|
-
title: "Pull request tracker",
|
|
2933
|
-
message: result.error.message
|
|
2934
|
-
});
|
|
2935
|
-
return;
|
|
2936
|
-
}
|
|
2937
|
-
switch (result.value) {
|
|
2938
|
-
case "refreshed":
|
|
2939
|
-
api.ui.toast({
|
|
2940
|
-
variant: "success",
|
|
2941
|
-
title: "Pull request tracker",
|
|
2942
|
-
message: "Pull request status synced"
|
|
2943
|
-
});
|
|
2944
|
-
return;
|
|
2945
|
-
case "no_attachments":
|
|
2946
|
-
api.ui.toast({
|
|
2947
|
-
variant: "info",
|
|
2948
|
-
title: "Pull request tracker",
|
|
2949
|
-
message: "No pull requests are attached"
|
|
2950
|
-
});
|
|
2951
|
-
return;
|
|
2952
|
-
case "stopped":
|
|
2953
|
-
api.ui.toast({
|
|
2954
|
-
variant: "error",
|
|
2955
|
-
title: "Pull request tracker",
|
|
2956
|
-
message: "Unable to refresh pull request status"
|
|
2957
|
-
});
|
|
2958
|
-
return;
|
|
2959
|
-
}
|
|
2960
|
-
} catch {
|
|
2961
|
-
api.ui.toast({
|
|
2962
|
-
variant: "error",
|
|
2963
|
-
title: "Pull request tracker",
|
|
2964
|
-
message: "Unable to refresh pull request status"
|
|
2965
|
-
});
|
|
2966
|
-
}
|
|
892
|
+
}),
|
|
893
|
+
PullRequest: ({
|
|
894
|
+
connector,
|
|
895
|
+
entry,
|
|
896
|
+
marker
|
|
897
|
+
}) => _$createComponent(PullRequestRow, {
|
|
898
|
+
get compact() {
|
|
899
|
+
return props.compact;
|
|
900
|
+
},
|
|
901
|
+
connector,
|
|
902
|
+
entry,
|
|
903
|
+
marker,
|
|
904
|
+
get onOpen() {
|
|
905
|
+
return props.onOpen;
|
|
906
|
+
},
|
|
907
|
+
get palette() {
|
|
908
|
+
return props.palette;
|
|
2967
909
|
}
|
|
2968
|
-
}
|
|
2969
|
-
bindings: []
|
|
2970
|
-
});
|
|
2971
|
-
api.lifecycle.onDispose(() => {
|
|
2972
|
-
disposeCommands();
|
|
2973
|
-
for (const disposeEvent of disposeEvents)
|
|
2974
|
-
disposeEvent();
|
|
910
|
+
})
|
|
2975
911
|
});
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/ui/Sidebar.tsx
|
|
915
|
+
var collapsibleAbove = 2;
|
|
916
|
+
var entriesOf = (view) => view.entries.map((entry) => ({
|
|
917
|
+
membership: Option8.fromNullishOr(entry.membership),
|
|
918
|
+
ref: entry.ref,
|
|
919
|
+
status: entry.status
|
|
920
|
+
}));
|
|
921
|
+
function Heading(props) {
|
|
922
|
+
return (() => {
|
|
923
|
+
var _el$ = _$createElement2("box"), _el$3 = _$createElement2("text"), _el$4 = _$createElement2("b");
|
|
924
|
+
_$insertNode2(_el$, _el$3);
|
|
925
|
+
_$setProp2(_el$, "flexDirection", "row");
|
|
926
|
+
_$setProp2(_el$, "gap", 1);
|
|
927
|
+
_$setProp2(_el$, "onMouseDown", () => {
|
|
928
|
+
if (props.collapsible)
|
|
929
|
+
props.onToggle();
|
|
930
|
+
});
|
|
931
|
+
_$insert2(_el$, _$createComponent2(Show2, {
|
|
932
|
+
get when() {
|
|
933
|
+
return props.collapsible;
|
|
934
|
+
},
|
|
935
|
+
get children() {
|
|
936
|
+
var _el$2 = _$createElement2("text");
|
|
937
|
+
_$insert2(_el$2, () => props.collapsed ? "\u25B6" : "\u25BC");
|
|
938
|
+
_$effect2((_$p) => _$setProp2(_el$2, "fg", props.palette.text, _$p));
|
|
939
|
+
return _el$2;
|
|
940
|
+
}
|
|
941
|
+
}), _el$3);
|
|
942
|
+
_$insertNode2(_el$3, _el$4);
|
|
943
|
+
_$insertNode2(_el$4, _$createTextNode2(`Pull requests`));
|
|
944
|
+
_$effect2((_$p) => _$setProp2(_el$3, "fg", props.palette.text, _$p));
|
|
945
|
+
return _el$;
|
|
946
|
+
})();
|
|
947
|
+
}
|
|
948
|
+
function Rows(props) {
|
|
949
|
+
return _$createComponent2(Show2, {
|
|
950
|
+
get fallback() {
|
|
951
|
+
return (() => {
|
|
952
|
+
var _el$7 = _$createElement2("text");
|
|
953
|
+
_$insertNode2(_el$7, _$createTextNode2(`No pull requests attached`));
|
|
954
|
+
_$effect2((_$p) => _$setProp2(_el$7, "fg", props.palette.muted, _$p));
|
|
955
|
+
return _el$7;
|
|
956
|
+
})();
|
|
957
|
+
},
|
|
958
|
+
get when() {
|
|
959
|
+
return props.view.entries.length > 0;
|
|
960
|
+
},
|
|
961
|
+
get children() {
|
|
962
|
+
var _el$6 = _$createElement2("box");
|
|
963
|
+
_$setProp2(_el$6, "flexDirection", "column");
|
|
964
|
+
_$insert2(_el$6, _$createComponent2(For2, {
|
|
965
|
+
get each() {
|
|
966
|
+
return layout(entriesOf(props.view));
|
|
967
|
+
},
|
|
968
|
+
children: (row) => _$createComponent2(SidebarRow, {
|
|
969
|
+
get compact() {
|
|
970
|
+
return props.view.layout === "compact";
|
|
2984
971
|
},
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
972
|
+
get onOpen() {
|
|
973
|
+
return props.onOpen;
|
|
974
|
+
},
|
|
975
|
+
get palette() {
|
|
976
|
+
return props.palette;
|
|
977
|
+
},
|
|
978
|
+
row
|
|
979
|
+
})
|
|
980
|
+
}));
|
|
981
|
+
return _el$6;
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
function Sidebar(props) {
|
|
986
|
+
const collapsible = () => props.state._tag === "Ready" && props.state.view.entries.length > collapsibleAbove;
|
|
987
|
+
const body = () => Match4.valueTags(props.state, {
|
|
988
|
+
Failed: ({
|
|
989
|
+
message
|
|
990
|
+
}) => (() => {
|
|
991
|
+
var _el$9 = _$createElement2("text");
|
|
992
|
+
_$insert2(_el$9, message);
|
|
993
|
+
_$effect2((_$p) => _$setProp2(_el$9, "fg", props.palette.tones.red, _$p));
|
|
994
|
+
return _el$9;
|
|
995
|
+
})(),
|
|
996
|
+
Loading: () => (() => {
|
|
997
|
+
var _el$0 = _$createElement2("text");
|
|
998
|
+
_$insertNode2(_el$0, _$createTextNode2(`Loading`));
|
|
999
|
+
_$effect2((_$p) => _$setProp2(_el$0, "fg", props.palette.muted, _$p));
|
|
1000
|
+
return _el$0;
|
|
1001
|
+
})(),
|
|
1002
|
+
Ready: ({
|
|
1003
|
+
view
|
|
1004
|
+
}) => _$createComponent2(Rows, {
|
|
1005
|
+
get onOpen() {
|
|
1006
|
+
return props.onOpen;
|
|
1007
|
+
},
|
|
1008
|
+
get palette() {
|
|
1009
|
+
return props.palette;
|
|
1010
|
+
},
|
|
1011
|
+
view
|
|
1012
|
+
})
|
|
1013
|
+
});
|
|
1014
|
+
return (() => {
|
|
1015
|
+
var _el$10 = _$createElement2("box");
|
|
1016
|
+
_$setProp2(_el$10, "flexDirection", "column");
|
|
1017
|
+
_$setProp2(_el$10, "gap", 1);
|
|
1018
|
+
_$insert2(_el$10, _$createComponent2(Heading, {
|
|
1019
|
+
get collapsed() {
|
|
1020
|
+
return props.collapsed;
|
|
1021
|
+
},
|
|
1022
|
+
get collapsible() {
|
|
1023
|
+
return collapsible();
|
|
1024
|
+
},
|
|
1025
|
+
get onToggle() {
|
|
1026
|
+
return props.onToggle;
|
|
1027
|
+
},
|
|
1028
|
+
get palette() {
|
|
1029
|
+
return props.palette;
|
|
1030
|
+
}
|
|
1031
|
+
}), null);
|
|
1032
|
+
_$insert2(_el$10, _$createComponent2(Show2, {
|
|
1033
|
+
get when() {
|
|
1034
|
+
return !collapsible() || !props.collapsed;
|
|
1035
|
+
},
|
|
1036
|
+
get children() {
|
|
1037
|
+
return body();
|
|
2988
1038
|
}
|
|
1039
|
+
}), null);
|
|
1040
|
+
return _el$10;
|
|
1041
|
+
})();
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// src/tui/SessionSidebar.tsx
|
|
1045
|
+
function newestFirst() {
|
|
1046
|
+
let latest = 0;
|
|
1047
|
+
return {
|
|
1048
|
+
isLatest: (request) => request === latest,
|
|
1049
|
+
next: () => {
|
|
1050
|
+
latest += 1;
|
|
1051
|
+
return latest;
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
function sessionView(sessionID, tracker, run) {
|
|
1056
|
+
const [state, setState] = createSignal2({
|
|
1057
|
+
_tag: "Loading"
|
|
1058
|
+
});
|
|
1059
|
+
const requests = newestFirst();
|
|
1060
|
+
onCleanup(tracker.onUpdate((view) => {
|
|
1061
|
+
if (view.sessionID !== sessionID())
|
|
1062
|
+
return;
|
|
1063
|
+
requests.next();
|
|
1064
|
+
setState({
|
|
1065
|
+
_tag: "Ready",
|
|
1066
|
+
view
|
|
1067
|
+
});
|
|
1068
|
+
}));
|
|
1069
|
+
createEffect(on(sessionID, (current) => {
|
|
1070
|
+
const request = requests.next();
|
|
1071
|
+
setState({
|
|
1072
|
+
_tag: "Loading"
|
|
1073
|
+
});
|
|
1074
|
+
run(Effect6.map(Effect6.result(tracker.list(current)), (result) => {
|
|
1075
|
+
if (!requests.isLatest(request))
|
|
1076
|
+
return;
|
|
1077
|
+
setState(result._tag === "Failure" ? {
|
|
1078
|
+
_tag: "Failed",
|
|
1079
|
+
message: result.failure.message
|
|
1080
|
+
} : {
|
|
1081
|
+
_tag: "Ready",
|
|
1082
|
+
view: result.success
|
|
1083
|
+
});
|
|
1084
|
+
}));
|
|
1085
|
+
}));
|
|
1086
|
+
return state;
|
|
1087
|
+
}
|
|
1088
|
+
function SessionSidebar(props) {
|
|
1089
|
+
const context = usePlugin2();
|
|
1090
|
+
const state = sessionView(() => props.sessionID, props.tracker, props.run);
|
|
1091
|
+
return _$createComponent3(Sidebar, {
|
|
1092
|
+
get collapsed() {
|
|
1093
|
+
return props.collapsed.has(props.sessionID);
|
|
1094
|
+
},
|
|
1095
|
+
get onOpen() {
|
|
1096
|
+
return props.onOpen;
|
|
1097
|
+
},
|
|
1098
|
+
onToggle: () => {
|
|
1099
|
+
props.collapsed.toggle(props.sessionID);
|
|
1100
|
+
},
|
|
1101
|
+
get palette() {
|
|
1102
|
+
return paletteOf(context.theme, context.themeMode);
|
|
1103
|
+
},
|
|
1104
|
+
get state() {
|
|
1105
|
+
return state();
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/tui/Terminal.ts
|
|
1111
|
+
import { Effect as Effect7, Option as Option9 } from "effect";
|
|
1112
|
+
function currentSession(ui) {
|
|
1113
|
+
const route = ui.router.current();
|
|
1114
|
+
return route.type === "session" ? Option9.some(route.sessionID) : Option9.none();
|
|
1115
|
+
}
|
|
1116
|
+
function hostTerminal(ui) {
|
|
1117
|
+
let open = false;
|
|
1118
|
+
const dialog = (show) => Effect7.acquireUseRelease(Effect7.sync(() => {
|
|
1119
|
+
open = true;
|
|
1120
|
+
}), () => Effect7.promise(show).pipe(Effect7.map(Option9.fromNullishOr)), () => Effect7.sync(() => {
|
|
1121
|
+
open = false;
|
|
1122
|
+
}));
|
|
1123
|
+
return {
|
|
1124
|
+
dismiss: () => {
|
|
1125
|
+
if (open)
|
|
1126
|
+
ui.dialog.clear();
|
|
1127
|
+
},
|
|
1128
|
+
terminal: {
|
|
1129
|
+
choose: (title, choices) => dialog(async () => {
|
|
1130
|
+
const chosen = await ui.dialog.select({ options: choices, title });
|
|
1131
|
+
return chosen;
|
|
1132
|
+
}),
|
|
1133
|
+
notify: (variant, message) => {
|
|
1134
|
+
ui.toast.show({ message, title: "Pull requests", variant });
|
|
1135
|
+
},
|
|
1136
|
+
prompt: (title, placeholder) => dialog(async () => {
|
|
1137
|
+
const answer = await ui.dialog.prompt({ placeholder, title });
|
|
1138
|
+
return answer;
|
|
1139
|
+
}),
|
|
1140
|
+
session: () => currentSession(ui)
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// src/tui.tsx
|
|
1146
|
+
var open = (url) => openUrl(url, process.platform).pipe(Effect8.provide(layer));
|
|
1147
|
+
var locationOfSession = (session) => Option10.fromNullishOr(session.location);
|
|
1148
|
+
function collapsedSessions() {
|
|
1149
|
+
const [collapsed, setCollapsed] = createSignal3(new Set);
|
|
1150
|
+
return {
|
|
1151
|
+
has: (sessionID) => collapsed().has(sessionID),
|
|
1152
|
+
toggle: (sessionID) => {
|
|
1153
|
+
const next = new Set(collapsed());
|
|
1154
|
+
if (!next.delete(sessionID))
|
|
1155
|
+
next.add(sessionID);
|
|
1156
|
+
setCollapsed(next);
|
|
2989
1157
|
}
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function trackerFor(context) {
|
|
1161
|
+
return makeClient({
|
|
1162
|
+
locationOf: (sessionID) => Option10.fromNullishOr(context.data.session.get(sessionID)).pipe(Option10.flatMap(locationOfSession), Option10.orElse(() => Option10.fromNullishOr(context.location))),
|
|
1163
|
+
rpc: context.client.rpc(PullRequestTracker)
|
|
2990
1164
|
});
|
|
2991
1165
|
}
|
|
2992
|
-
var
|
|
1166
|
+
var tui_default = Plugin.define({
|
|
2993
1167
|
id: "opencode-pr-tracker",
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
1168
|
+
setup(context) {
|
|
1169
|
+
const tracker = trackerFor(context);
|
|
1170
|
+
const {
|
|
1171
|
+
dismiss,
|
|
1172
|
+
terminal
|
|
1173
|
+
} = hostTerminal(context.ui);
|
|
1174
|
+
const commands = actions({
|
|
1175
|
+
open,
|
|
1176
|
+
terminal,
|
|
1177
|
+
tracker
|
|
3000
1178
|
});
|
|
1179
|
+
const collapsed = collapsedSessions();
|
|
1180
|
+
const tasks = background();
|
|
1181
|
+
const disposers = [context.ui.slot({
|
|
1182
|
+
append: "app",
|
|
1183
|
+
render: () => _$createComponent4(Commands, {
|
|
1184
|
+
actions: commands,
|
|
1185
|
+
get run() {
|
|
1186
|
+
return tasks.run;
|
|
1187
|
+
}
|
|
1188
|
+
})
|
|
1189
|
+
}), context.ui.slot({
|
|
1190
|
+
append: "sidebar.content",
|
|
1191
|
+
render: ({
|
|
1192
|
+
sessionID
|
|
1193
|
+
}) => _$createComponent4(SessionSidebar, {
|
|
1194
|
+
collapsed,
|
|
1195
|
+
onOpen: (ref) => {
|
|
1196
|
+
tasks.run(commands.openPullRequest(ref));
|
|
1197
|
+
},
|
|
1198
|
+
get run() {
|
|
1199
|
+
return tasks.run;
|
|
1200
|
+
},
|
|
1201
|
+
sessionID,
|
|
1202
|
+
tracker
|
|
1203
|
+
})
|
|
1204
|
+
})];
|
|
1205
|
+
return async () => {
|
|
1206
|
+
for (const dispose of disposers)
|
|
1207
|
+
dispose();
|
|
1208
|
+
dismiss();
|
|
1209
|
+
await tasks.stop();
|
|
1210
|
+
};
|
|
3001
1211
|
}
|
|
3002
|
-
};
|
|
3003
|
-
var tui_default = plugin;
|
|
1212
|
+
});
|
|
3004
1213
|
export {
|
|
3005
|
-
|
|
3006
|
-
registerTui,
|
|
3007
|
-
openPullRequest,
|
|
3008
|
-
tui_default as default,
|
|
3009
|
-
attachPullRequest
|
|
1214
|
+
tui_default as default
|
|
3010
1215
|
};
|
|
3011
1216
|
|
|
3012
|
-
//# debugId=
|
|
1217
|
+
//# debugId=6F713F5E90CA6B2064756E2164756E21
|