@great-arrow/cli 0.7.2 → 0.8.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 +19 -0
- package/dist/index.js +1606 -264
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
44
44
|
// node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js
|
|
45
45
|
var require_polyfills = __commonJS({
|
|
46
46
|
"node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
47
|
-
var
|
|
47
|
+
var constants3 = __require("constants");
|
|
48
48
|
var origCwd = process.cwd;
|
|
49
49
|
var cwd = null;
|
|
50
50
|
var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
@@ -68,7 +68,7 @@ var require_polyfills = __commonJS({
|
|
|
68
68
|
var chdir;
|
|
69
69
|
module.exports = patch;
|
|
70
70
|
function patch(fs6) {
|
|
71
|
-
if (
|
|
71
|
+
if (constants3.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
72
72
|
patchLchmod(fs6);
|
|
73
73
|
}
|
|
74
74
|
if (!fs6.lutimes) {
|
|
@@ -108,7 +108,7 @@ var require_polyfills = __commonJS({
|
|
|
108
108
|
}
|
|
109
109
|
if (platform2 === "win32") {
|
|
110
110
|
fs6.rename = typeof fs6.rename !== "function" ? fs6.rename : (function(fs$rename) {
|
|
111
|
-
function
|
|
111
|
+
function rename5(from, to, cb) {
|
|
112
112
|
var start = Date.now();
|
|
113
113
|
var backoff = 0;
|
|
114
114
|
fs$rename(from, to, function CB(er) {
|
|
@@ -128,8 +128,8 @@ var require_polyfills = __commonJS({
|
|
|
128
128
|
if (cb) cb(er);
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
|
-
if (Object.setPrototypeOf) Object.setPrototypeOf(
|
|
132
|
-
return
|
|
131
|
+
if (Object.setPrototypeOf) Object.setPrototypeOf(rename5, fs$rename);
|
|
132
|
+
return rename5;
|
|
133
133
|
})(fs6.rename);
|
|
134
134
|
}
|
|
135
135
|
fs6.read = typeof fs6.read !== "function" ? fs6.read : (function(fs$read) {
|
|
@@ -170,7 +170,7 @@ var require_polyfills = __commonJS({
|
|
|
170
170
|
fs7.lchmod = function(path10, mode, callback) {
|
|
171
171
|
fs7.open(
|
|
172
172
|
path10,
|
|
173
|
-
|
|
173
|
+
constants3.O_WRONLY | constants3.O_SYMLINK,
|
|
174
174
|
mode,
|
|
175
175
|
function(err2, fd) {
|
|
176
176
|
if (err2) {
|
|
@@ -186,7 +186,7 @@ var require_polyfills = __commonJS({
|
|
|
186
186
|
);
|
|
187
187
|
};
|
|
188
188
|
fs7.lchmodSync = function(path10, mode) {
|
|
189
|
-
var fd = fs7.openSync(path10,
|
|
189
|
+
var fd = fs7.openSync(path10, constants3.O_WRONLY | constants3.O_SYMLINK, mode);
|
|
190
190
|
var threw = true;
|
|
191
191
|
var ret;
|
|
192
192
|
try {
|
|
@@ -206,9 +206,9 @@ var require_polyfills = __commonJS({
|
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
208
|
function patchLutimes(fs7) {
|
|
209
|
-
if (
|
|
209
|
+
if (constants3.hasOwnProperty("O_SYMLINK") && fs7.futimes) {
|
|
210
210
|
fs7.lutimes = function(path10, at, mt, cb) {
|
|
211
|
-
fs7.open(path10,
|
|
211
|
+
fs7.open(path10, constants3.O_SYMLINK, function(er, fd) {
|
|
212
212
|
if (er) {
|
|
213
213
|
if (cb) cb(er);
|
|
214
214
|
return;
|
|
@@ -221,7 +221,7 @@ var require_polyfills = __commonJS({
|
|
|
221
221
|
});
|
|
222
222
|
};
|
|
223
223
|
fs7.lutimesSync = function(path10, at, mt) {
|
|
224
|
-
var fd = fs7.openSync(path10,
|
|
224
|
+
var fd = fs7.openSync(path10, constants3.O_SYMLINK);
|
|
225
225
|
var ret;
|
|
226
226
|
var threw = true;
|
|
227
227
|
try {
|
|
@@ -502,14 +502,14 @@ var require_graceful_fs = __commonJS({
|
|
|
502
502
|
return close;
|
|
503
503
|
})(fs6.close);
|
|
504
504
|
fs6.closeSync = (function(fs$closeSync) {
|
|
505
|
-
function
|
|
505
|
+
function closeSync2(fd) {
|
|
506
506
|
fs$closeSync.apply(fs6, arguments);
|
|
507
507
|
resetQueue();
|
|
508
508
|
}
|
|
509
|
-
Object.defineProperty(
|
|
509
|
+
Object.defineProperty(closeSync2, previousSymbol, {
|
|
510
510
|
value: fs$closeSync
|
|
511
511
|
});
|
|
512
|
-
return
|
|
512
|
+
return closeSync2;
|
|
513
513
|
})(fs6.closeSync);
|
|
514
514
|
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
|
|
515
515
|
process.on("exit", function() {
|
|
@@ -533,8 +533,8 @@ var require_graceful_fs = __commonJS({
|
|
|
533
533
|
fs7.createReadStream = createReadStream;
|
|
534
534
|
fs7.createWriteStream = createWriteStream;
|
|
535
535
|
var fs$readFile = fs7.readFile;
|
|
536
|
-
fs7.readFile =
|
|
537
|
-
function
|
|
536
|
+
fs7.readFile = readFile11;
|
|
537
|
+
function readFile11(path10, options, cb) {
|
|
538
538
|
if (typeof options === "function")
|
|
539
539
|
cb = options, options = null;
|
|
540
540
|
return go$readFile(path10, options, cb);
|
|
@@ -550,8 +550,8 @@ var require_graceful_fs = __commonJS({
|
|
|
550
550
|
}
|
|
551
551
|
}
|
|
552
552
|
var fs$writeFile = fs7.writeFile;
|
|
553
|
-
fs7.writeFile =
|
|
554
|
-
function
|
|
553
|
+
fs7.writeFile = writeFile10;
|
|
554
|
+
function writeFile10(path10, data, options, cb) {
|
|
555
555
|
if (typeof options === "function")
|
|
556
556
|
cb = options, options = null;
|
|
557
557
|
return go$writeFile(path10, data, options, cb);
|
|
@@ -1155,8 +1155,8 @@ var require_semver = __commonJS({
|
|
|
1155
1155
|
}
|
|
1156
1156
|
// preminor will bump the version up to the next minor release, and immediately
|
|
1157
1157
|
// down to pre-release. premajor and prepatch work the same way.
|
|
1158
|
-
inc(
|
|
1159
|
-
if (
|
|
1158
|
+
inc(release2, identifier, identifierBase) {
|
|
1159
|
+
if (release2.startsWith("pre")) {
|
|
1160
1160
|
if (!identifier && identifierBase === false) {
|
|
1161
1161
|
throw new Error("invalid increment argument: identifier is empty");
|
|
1162
1162
|
}
|
|
@@ -1167,7 +1167,7 @@ var require_semver = __commonJS({
|
|
|
1167
1167
|
}
|
|
1168
1168
|
}
|
|
1169
1169
|
}
|
|
1170
|
-
switch (
|
|
1170
|
+
switch (release2) {
|
|
1171
1171
|
case "premajor":
|
|
1172
1172
|
this.prerelease.length = 0;
|
|
1173
1173
|
this.patch = 0;
|
|
@@ -1258,7 +1258,7 @@ var require_semver = __commonJS({
|
|
|
1258
1258
|
break;
|
|
1259
1259
|
}
|
|
1260
1260
|
default:
|
|
1261
|
-
throw new Error(`invalid increment argument: ${
|
|
1261
|
+
throw new Error(`invalid increment argument: ${release2}`);
|
|
1262
1262
|
}
|
|
1263
1263
|
this.raw = this.format();
|
|
1264
1264
|
if (this.build.length) {
|
|
@@ -1968,7 +1968,7 @@ var require_minimist = __commonJS({
|
|
|
1968
1968
|
var require_rc = __commonJS({
|
|
1969
1969
|
"node_modules/.pnpm/rc@1.2.8/node_modules/rc/index.js"(exports, module) {
|
|
1970
1970
|
var cc = require_utils();
|
|
1971
|
-
var
|
|
1971
|
+
var join18 = __require("path").join;
|
|
1972
1972
|
var deepExtend = require_deep_extend();
|
|
1973
1973
|
var etc = "/etc";
|
|
1974
1974
|
var win = process.platform === "win32";
|
|
@@ -1993,15 +1993,15 @@ var require_rc = __commonJS({
|
|
|
1993
1993
|
}
|
|
1994
1994
|
if (!win)
|
|
1995
1995
|
[
|
|
1996
|
-
|
|
1997
|
-
|
|
1996
|
+
join18(etc, name23, "config"),
|
|
1997
|
+
join18(etc, name23 + "rc")
|
|
1998
1998
|
].forEach(addConfigFile);
|
|
1999
1999
|
if (home)
|
|
2000
2000
|
[
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2001
|
+
join18(home, ".config", name23, "config"),
|
|
2002
|
+
join18(home, ".config", name23),
|
|
2003
|
+
join18(home, "." + name23, "config"),
|
|
2004
|
+
join18(home, "." + name23 + "rc")
|
|
2005
2005
|
].forEach(addConfigFile);
|
|
2006
2006
|
addConfigFile(cc.find("." + name23 + "rc"));
|
|
2007
2007
|
if (env4.config) addConfigFile(env4.config);
|
|
@@ -2018,7 +2018,7 @@ var require_rc = __commonJS({
|
|
|
2018
2018
|
// node_modules/.pnpm/graceful-fs@4.2.10/node_modules/graceful-fs/polyfills.js
|
|
2019
2019
|
var require_polyfills2 = __commonJS({
|
|
2020
2020
|
"node_modules/.pnpm/graceful-fs@4.2.10/node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
2021
|
-
var
|
|
2021
|
+
var constants3 = __require("constants");
|
|
2022
2022
|
var origCwd = process.cwd;
|
|
2023
2023
|
var cwd = null;
|
|
2024
2024
|
var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
@@ -2042,7 +2042,7 @@ var require_polyfills2 = __commonJS({
|
|
|
2042
2042
|
var chdir;
|
|
2043
2043
|
module.exports = patch;
|
|
2044
2044
|
function patch(fs6) {
|
|
2045
|
-
if (
|
|
2045
|
+
if (constants3.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
2046
2046
|
patchLchmod(fs6);
|
|
2047
2047
|
}
|
|
2048
2048
|
if (!fs6.lutimes) {
|
|
@@ -2082,7 +2082,7 @@ var require_polyfills2 = __commonJS({
|
|
|
2082
2082
|
}
|
|
2083
2083
|
if (platform2 === "win32") {
|
|
2084
2084
|
fs6.rename = typeof fs6.rename !== "function" ? fs6.rename : (function(fs$rename) {
|
|
2085
|
-
function
|
|
2085
|
+
function rename5(from, to, cb) {
|
|
2086
2086
|
var start = Date.now();
|
|
2087
2087
|
var backoff = 0;
|
|
2088
2088
|
fs$rename(from, to, function CB(er) {
|
|
@@ -2102,8 +2102,8 @@ var require_polyfills2 = __commonJS({
|
|
|
2102
2102
|
if (cb) cb(er);
|
|
2103
2103
|
});
|
|
2104
2104
|
}
|
|
2105
|
-
if (Object.setPrototypeOf) Object.setPrototypeOf(
|
|
2106
|
-
return
|
|
2105
|
+
if (Object.setPrototypeOf) Object.setPrototypeOf(rename5, fs$rename);
|
|
2106
|
+
return rename5;
|
|
2107
2107
|
})(fs6.rename);
|
|
2108
2108
|
}
|
|
2109
2109
|
fs6.read = typeof fs6.read !== "function" ? fs6.read : (function(fs$read) {
|
|
@@ -2144,7 +2144,7 @@ var require_polyfills2 = __commonJS({
|
|
|
2144
2144
|
fs7.lchmod = function(path10, mode, callback) {
|
|
2145
2145
|
fs7.open(
|
|
2146
2146
|
path10,
|
|
2147
|
-
|
|
2147
|
+
constants3.O_WRONLY | constants3.O_SYMLINK,
|
|
2148
2148
|
mode,
|
|
2149
2149
|
function(err2, fd) {
|
|
2150
2150
|
if (err2) {
|
|
@@ -2160,7 +2160,7 @@ var require_polyfills2 = __commonJS({
|
|
|
2160
2160
|
);
|
|
2161
2161
|
};
|
|
2162
2162
|
fs7.lchmodSync = function(path10, mode) {
|
|
2163
|
-
var fd = fs7.openSync(path10,
|
|
2163
|
+
var fd = fs7.openSync(path10, constants3.O_WRONLY | constants3.O_SYMLINK, mode);
|
|
2164
2164
|
var threw = true;
|
|
2165
2165
|
var ret;
|
|
2166
2166
|
try {
|
|
@@ -2180,9 +2180,9 @@ var require_polyfills2 = __commonJS({
|
|
|
2180
2180
|
};
|
|
2181
2181
|
}
|
|
2182
2182
|
function patchLutimes(fs7) {
|
|
2183
|
-
if (
|
|
2183
|
+
if (constants3.hasOwnProperty("O_SYMLINK") && fs7.futimes) {
|
|
2184
2184
|
fs7.lutimes = function(path10, at, mt, cb) {
|
|
2185
|
-
fs7.open(path10,
|
|
2185
|
+
fs7.open(path10, constants3.O_SYMLINK, function(er, fd) {
|
|
2186
2186
|
if (er) {
|
|
2187
2187
|
if (cb) cb(er);
|
|
2188
2188
|
return;
|
|
@@ -2195,7 +2195,7 @@ var require_polyfills2 = __commonJS({
|
|
|
2195
2195
|
});
|
|
2196
2196
|
};
|
|
2197
2197
|
fs7.lutimesSync = function(path10, at, mt) {
|
|
2198
|
-
var fd = fs7.openSync(path10,
|
|
2198
|
+
var fd = fs7.openSync(path10, constants3.O_SYMLINK);
|
|
2199
2199
|
var ret;
|
|
2200
2200
|
var threw = true;
|
|
2201
2201
|
try {
|
|
@@ -2476,14 +2476,14 @@ var require_graceful_fs2 = __commonJS({
|
|
|
2476
2476
|
return close;
|
|
2477
2477
|
})(fs6.close);
|
|
2478
2478
|
fs6.closeSync = (function(fs$closeSync) {
|
|
2479
|
-
function
|
|
2479
|
+
function closeSync2(fd) {
|
|
2480
2480
|
fs$closeSync.apply(fs6, arguments);
|
|
2481
2481
|
resetQueue();
|
|
2482
2482
|
}
|
|
2483
|
-
Object.defineProperty(
|
|
2483
|
+
Object.defineProperty(closeSync2, previousSymbol, {
|
|
2484
2484
|
value: fs$closeSync
|
|
2485
2485
|
});
|
|
2486
|
-
return
|
|
2486
|
+
return closeSync2;
|
|
2487
2487
|
})(fs6.closeSync);
|
|
2488
2488
|
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
|
|
2489
2489
|
process.on("exit", function() {
|
|
@@ -2507,8 +2507,8 @@ var require_graceful_fs2 = __commonJS({
|
|
|
2507
2507
|
fs7.createReadStream = createReadStream;
|
|
2508
2508
|
fs7.createWriteStream = createWriteStream;
|
|
2509
2509
|
var fs$readFile = fs7.readFile;
|
|
2510
|
-
fs7.readFile =
|
|
2511
|
-
function
|
|
2510
|
+
fs7.readFile = readFile11;
|
|
2511
|
+
function readFile11(path10, options, cb) {
|
|
2512
2512
|
if (typeof options === "function")
|
|
2513
2513
|
cb = options, options = null;
|
|
2514
2514
|
return go$readFile(path10, options, cb);
|
|
@@ -2524,8 +2524,8 @@ var require_graceful_fs2 = __commonJS({
|
|
|
2524
2524
|
}
|
|
2525
2525
|
}
|
|
2526
2526
|
var fs$writeFile = fs7.writeFile;
|
|
2527
|
-
fs7.writeFile =
|
|
2528
|
-
function
|
|
2527
|
+
fs7.writeFile = writeFile10;
|
|
2528
|
+
function writeFile10(path10, data, options, cb) {
|
|
2529
2529
|
if (typeof options === "function")
|
|
2530
2530
|
cb = options, options = null;
|
|
2531
2531
|
return go$writeFile(path10, data, options, cb);
|
|
@@ -3339,11 +3339,11 @@ var require_util = __commonJS({
|
|
|
3339
3339
|
if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
|
|
3340
3340
|
return name24;
|
|
3341
3341
|
}
|
|
3342
|
-
const
|
|
3343
|
-
if (
|
|
3342
|
+
const dirname10 = path10.dirname(name24);
|
|
3343
|
+
if (dirname10 === name24) {
|
|
3344
3344
|
return original;
|
|
3345
3345
|
}
|
|
3346
|
-
return find(
|
|
3346
|
+
return find(dirname10, original);
|
|
3347
3347
|
} catch (error62) {
|
|
3348
3348
|
if (name24 === original) {
|
|
3349
3349
|
if (error62.code === "ENOENT") {
|
|
@@ -4044,7 +4044,7 @@ var require_inc = __commonJS({
|
|
|
4044
4044
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/inc.js"(exports, module) {
|
|
4045
4045
|
"use strict";
|
|
4046
4046
|
var SemVer = require_semver();
|
|
4047
|
-
var inc = (version2,
|
|
4047
|
+
var inc = (version2, release2, options, identifier, identifierBase) => {
|
|
4048
4048
|
if (typeof options === "string") {
|
|
4049
4049
|
identifierBase = identifier;
|
|
4050
4050
|
identifier = options;
|
|
@@ -4054,7 +4054,7 @@ var require_inc = __commonJS({
|
|
|
4054
4054
|
return new SemVer(
|
|
4055
4055
|
version2 instanceof SemVer ? version2.version : version2,
|
|
4056
4056
|
options
|
|
4057
|
-
).inc(
|
|
4057
|
+
).inc(release2, identifier, identifierBase).version;
|
|
4058
4058
|
} catch (er) {
|
|
4059
4059
|
return null;
|
|
4060
4060
|
}
|
|
@@ -4311,10 +4311,10 @@ var require_truncate = __commonJS({
|
|
|
4311
4311
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/truncate.js"(exports, module) {
|
|
4312
4312
|
"use strict";
|
|
4313
4313
|
var parse3 = require_parse();
|
|
4314
|
-
var
|
|
4314
|
+
var constants3 = require_constants();
|
|
4315
4315
|
var SemVer = require_semver();
|
|
4316
4316
|
var truncate = (version2, truncation, options) => {
|
|
4317
|
-
if (!
|
|
4317
|
+
if (!constants3.RELEASE_TYPES.includes(truncation)) {
|
|
4318
4318
|
return null;
|
|
4319
4319
|
}
|
|
4320
4320
|
const clonedVersion = cloneInputVersion(version2, options);
|
|
@@ -5358,7 +5358,7 @@ var require_semver2 = __commonJS({
|
|
|
5358
5358
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/index.js"(exports, module) {
|
|
5359
5359
|
"use strict";
|
|
5360
5360
|
var internalRe = require_re();
|
|
5361
|
-
var
|
|
5361
|
+
var constants3 = require_constants();
|
|
5362
5362
|
var SemVer = require_semver();
|
|
5363
5363
|
var identifiers = require_identifiers();
|
|
5364
5364
|
var parse3 = require_parse();
|
|
@@ -5442,8 +5442,8 @@ var require_semver2 = __commonJS({
|
|
|
5442
5442
|
re: internalRe.re,
|
|
5443
5443
|
src: internalRe.src,
|
|
5444
5444
|
tokens: internalRe.t,
|
|
5445
|
-
SEMVER_SPEC_VERSION:
|
|
5446
|
-
RELEASE_TYPES:
|
|
5445
|
+
SEMVER_SPEC_VERSION: constants3.SEMVER_SPEC_VERSION,
|
|
5446
|
+
RELEASE_TYPES: constants3.RELEASE_TYPES,
|
|
5447
5447
|
compareIdentifiers: identifiers.compareIdentifiers,
|
|
5448
5448
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
5449
5449
|
};
|
|
@@ -8515,7 +8515,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
8515
8515
|
const negativeNumberArg = (arg) => {
|
|
8516
8516
|
if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg)) return false;
|
|
8517
8517
|
return !this._getCommandAndAncestors().some(
|
|
8518
|
-
(cmd) => cmd.options.map((opt) => opt.short).some((
|
|
8518
|
+
(cmd) => cmd.options.map((opt) => opt.short).some((short2) => /^-\d$/.test(short2))
|
|
8519
8519
|
);
|
|
8520
8520
|
};
|
|
8521
8521
|
let activeVariadicOption = null;
|
|
@@ -9483,7 +9483,7 @@ function isNodeError(value) {
|
|
|
9483
9483
|
|
|
9484
9484
|
// src/lib/version.ts
|
|
9485
9485
|
var CLI_PACKAGE_NAME = "@great-arrow/cli";
|
|
9486
|
-
var CLI_VERSION = "0.
|
|
9486
|
+
var CLI_VERSION = "0.8.0";
|
|
9487
9487
|
var GAD_INSTALL_VERSION = "2026.08.08";
|
|
9488
9488
|
var GAD_INSTALL_MIN_VERSION = "2026.06.22";
|
|
9489
9489
|
var GAD_INSTALL_HEADER = "X-GAD-Install-Version";
|
|
@@ -10056,12 +10056,12 @@ h1{font-size:1.25rem;margin:0 0 .5rem}p{color:#9a9aa3;margin:0}</style></head>
|
|
|
10056
10056
|
<body><div class="card"><h1>${ok ? "\u2713 Signed in to Great Arrow" : "\u2717 Sign-in failed"}</h1>
|
|
10057
10057
|
<p>${escapeHtml(detail)}</p></div></body></html>`;
|
|
10058
10058
|
function listen(port) {
|
|
10059
|
-
return new Promise((
|
|
10059
|
+
return new Promise((resolve7, reject) => {
|
|
10060
10060
|
const server = createServer();
|
|
10061
10061
|
server.on("error", reject);
|
|
10062
10062
|
server.listen(port, LOOPBACK_HOST, () => {
|
|
10063
10063
|
const addr = server.address();
|
|
10064
|
-
|
|
10064
|
+
resolve7({ server, port: addr.port });
|
|
10065
10065
|
});
|
|
10066
10066
|
});
|
|
10067
10067
|
}
|
|
@@ -10086,7 +10086,7 @@ async function startLoopbackServer(opts) {
|
|
|
10086
10086
|
port,
|
|
10087
10087
|
close: () => server.close(),
|
|
10088
10088
|
waitForCallback({ timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
|
|
10089
|
-
return new Promise((
|
|
10089
|
+
return new Promise((resolve7, reject) => {
|
|
10090
10090
|
const timer = setTimeout(() => {
|
|
10091
10091
|
server.close();
|
|
10092
10092
|
reject(new Error("Timed out waiting for the browser callback."));
|
|
@@ -10126,7 +10126,7 @@ async function startLoopbackServer(opts) {
|
|
|
10126
10126
|
"You can close this tab and return to your terminal."
|
|
10127
10127
|
)
|
|
10128
10128
|
);
|
|
10129
|
-
settle2(() =>
|
|
10129
|
+
settle2(() => resolve7({ code, state }));
|
|
10130
10130
|
});
|
|
10131
10131
|
});
|
|
10132
10132
|
}
|
|
@@ -10411,11 +10411,11 @@ async function confirm(message) {
|
|
|
10411
10411
|
input: process.stdin,
|
|
10412
10412
|
output: process.stdout
|
|
10413
10413
|
});
|
|
10414
|
-
return new Promise((
|
|
10414
|
+
return new Promise((resolve7) => {
|
|
10415
10415
|
rl.question(`${message} [y/N] `, (answer) => {
|
|
10416
10416
|
rl.close();
|
|
10417
10417
|
const normalized = answer.trim().toLowerCase();
|
|
10418
|
-
|
|
10418
|
+
resolve7(normalized === "y" || normalized === "yes");
|
|
10419
10419
|
});
|
|
10420
10420
|
});
|
|
10421
10421
|
}
|
|
@@ -10590,8 +10590,8 @@ function createFormatter(options) {
|
|
|
10590
10590
|
var DEFAULT_API_URL = "https://www.greatarrow.ai";
|
|
10591
10591
|
function isLegacyApiUrl(value) {
|
|
10592
10592
|
try {
|
|
10593
|
-
const
|
|
10594
|
-
return
|
|
10593
|
+
const hostname7 = new URL(value).hostname.toLowerCase();
|
|
10594
|
+
return hostname7 === "www.greatarrowdigital.com" || hostname7 === "greatarrowdigital.com";
|
|
10595
10595
|
} catch {
|
|
10596
10596
|
return false;
|
|
10597
10597
|
}
|
|
@@ -10611,10 +10611,10 @@ function maskToken(token) {
|
|
|
10611
10611
|
}
|
|
10612
10612
|
function openBrowser(url2) {
|
|
10613
10613
|
if (!/^https?:\/\//i.test(url2)) return Promise.resolve(false);
|
|
10614
|
-
return new Promise((
|
|
10614
|
+
return new Promise((resolve7) => {
|
|
10615
10615
|
const platform2 = process.platform;
|
|
10616
10616
|
const [cmd, args] = platform2 === "darwin" ? ["open", [url2]] : platform2 === "win32" ? ["cmd", ["/c", "start", "", url2]] : ["xdg-open", [url2]];
|
|
10617
|
-
execFile(cmd, args, (error62) =>
|
|
10617
|
+
execFile(cmd, args, (error62) => resolve7(!error62));
|
|
10618
10618
|
});
|
|
10619
10619
|
}
|
|
10620
10620
|
var loginCommand = new Command("login").description("Authenticate the CLI with your Great Arrow Digital account").option(
|
|
@@ -11364,7 +11364,7 @@ function getCommandWorkspaceTarget(options = {}) {
|
|
|
11364
11364
|
|
|
11365
11365
|
// ../local-runtime/src/abortable.ts
|
|
11366
11366
|
function abortable(run4, signal) {
|
|
11367
|
-
return new Promise((
|
|
11367
|
+
return new Promise((resolve7, reject) => {
|
|
11368
11368
|
const abort = () => {
|
|
11369
11369
|
signal.removeEventListener("abort", abort);
|
|
11370
11370
|
reject(signal.reason ?? new Error("Local operation stopped"));
|
|
@@ -11374,7 +11374,7 @@ function abortable(run4, signal) {
|
|
|
11374
11374
|
Promise.resolve().then(() => {
|
|
11375
11375
|
signal.throwIfAborted();
|
|
11376
11376
|
return run4();
|
|
11377
|
-
}).then(
|
|
11377
|
+
}).then(resolve7, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
11378
11378
|
});
|
|
11379
11379
|
}
|
|
11380
11380
|
async function withLocalDeadline(deadlineMs, run4, parentSignal) {
|
|
@@ -11634,7 +11634,7 @@ function describeError(error62) {
|
|
|
11634
11634
|
return String(error62);
|
|
11635
11635
|
}
|
|
11636
11636
|
function sleep(ms) {
|
|
11637
|
-
return new Promise((
|
|
11637
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
11638
11638
|
}
|
|
11639
11639
|
var GadClient = class {
|
|
11640
11640
|
token;
|
|
@@ -11759,11 +11759,11 @@ var GadClient = class {
|
|
|
11759
11759
|
const status = response.status;
|
|
11760
11760
|
const body = await response.text().catch(() => "");
|
|
11761
11761
|
if (status === 403) {
|
|
11762
|
-
const
|
|
11763
|
-
if (
|
|
11762
|
+
const refusal3 = policyRefusal(body, this.apiUrl);
|
|
11763
|
+
if (refusal3) {
|
|
11764
11764
|
throw withServerCode(
|
|
11765
|
-
new CliError(
|
|
11766
|
-
JSON.stringify({ error:
|
|
11765
|
+
new CliError(refusal3.message, 1 /* AUTH_ERROR */, refusal3.hint),
|
|
11766
|
+
JSON.stringify({ error: refusal3.code })
|
|
11767
11767
|
);
|
|
11768
11768
|
}
|
|
11769
11769
|
throw withServerCode(
|
|
@@ -11858,9 +11858,9 @@ var GadClient = class {
|
|
|
11858
11858
|
message: data.error.message
|
|
11859
11859
|
}
|
|
11860
11860
|
});
|
|
11861
|
-
const
|
|
11862
|
-
if (
|
|
11863
|
-
throw new CliError(
|
|
11861
|
+
const refusal3 = policyRefusal(data, this.apiUrl);
|
|
11862
|
+
if (refusal3) {
|
|
11863
|
+
throw new CliError(refusal3.message, 1 /* AUTH_ERROR */, refusal3.hint);
|
|
11864
11864
|
}
|
|
11865
11865
|
const code = data.error.code === -32001 ? 1 /* AUTH_ERROR */ : 2 /* NETWORK_ERROR */;
|
|
11866
11866
|
throw new CliError(
|
|
@@ -11967,13 +11967,13 @@ var GadClient = class {
|
|
|
11967
11967
|
if (this.getToken) {
|
|
11968
11968
|
const getToken2 = this.getToken;
|
|
11969
11969
|
headers.Authorization = `Bearer ${await new Promise(
|
|
11970
|
-
(
|
|
11970
|
+
(resolve7, reject) => {
|
|
11971
11971
|
signal.addEventListener(
|
|
11972
11972
|
"abort",
|
|
11973
11973
|
() => reject(networkError("Session renewal timed out.")),
|
|
11974
11974
|
{ once: true }
|
|
11975
11975
|
);
|
|
11976
|
-
getToken2().then(
|
|
11976
|
+
getToken2().then(resolve7, reject);
|
|
11977
11977
|
}
|
|
11978
11978
|
)}`;
|
|
11979
11979
|
}
|
|
@@ -12192,10 +12192,10 @@ ${tryThese.map((t) => ` \u2022 ${t}`).join("\n")}`
|
|
|
12192
12192
|
|
|
12193
12193
|
// src/lib/stdin.ts
|
|
12194
12194
|
function readStream(stream) {
|
|
12195
|
-
return new Promise((
|
|
12195
|
+
return new Promise((resolve7, reject) => {
|
|
12196
12196
|
const chunks = [];
|
|
12197
12197
|
stream.on("data", (chunk) => chunks.push(chunk));
|
|
12198
|
-
stream.on("end", () =>
|
|
12198
|
+
stream.on("end", () => resolve7(Buffer.concat(chunks).toString("utf8")));
|
|
12199
12199
|
stream.on("error", reject);
|
|
12200
12200
|
});
|
|
12201
12201
|
}
|
|
@@ -13130,7 +13130,7 @@ function git(args, cwd) {
|
|
|
13130
13130
|
var WORKTREE_STATUS_CONCURRENCY = 8;
|
|
13131
13131
|
var WORKTREE_STATUS_TIMEOUT_MS = 2e3;
|
|
13132
13132
|
function gitAsync(args, cwd) {
|
|
13133
|
-
return new Promise((
|
|
13133
|
+
return new Promise((resolve7) => {
|
|
13134
13134
|
execFile2(
|
|
13135
13135
|
"git",
|
|
13136
13136
|
args,
|
|
@@ -13141,7 +13141,7 @@ function gitAsync(args, cwd) {
|
|
|
13141
13141
|
timeout: WORKTREE_STATUS_TIMEOUT_MS
|
|
13142
13142
|
},
|
|
13143
13143
|
(error62, stdout, stderr) => {
|
|
13144
|
-
|
|
13144
|
+
resolve7({
|
|
13145
13145
|
status: error62 ? 1 : 0,
|
|
13146
13146
|
stdout: stdout ?? "",
|
|
13147
13147
|
stderr: stderr ?? "",
|
|
@@ -16941,8 +16941,8 @@ async function runStdioBridge(options) {
|
|
|
16941
16941
|
if (response) writeLine(response);
|
|
16942
16942
|
});
|
|
16943
16943
|
});
|
|
16944
|
-
await new Promise((
|
|
16945
|
-
rl.on("close", () =>
|
|
16944
|
+
await new Promise((resolve7) => {
|
|
16945
|
+
rl.on("close", () => resolve7());
|
|
16946
16946
|
});
|
|
16947
16947
|
}
|
|
16948
16948
|
|
|
@@ -17065,9 +17065,9 @@ var CLIENT_IDS = {
|
|
|
17065
17065
|
};
|
|
17066
17066
|
async function mcpJsonAtRiskOfCommit(dir) {
|
|
17067
17067
|
try {
|
|
17068
|
-
const { execFile:
|
|
17068
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
17069
17069
|
const { promisify: promisify2 } = await import("node:util");
|
|
17070
|
-
const run4 = promisify2(
|
|
17070
|
+
const run4 = promisify2(execFile6);
|
|
17071
17071
|
await run4("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dir });
|
|
17072
17072
|
try {
|
|
17073
17073
|
await run4("git", ["check-ignore", "-q", ".mcp.json"], { cwd: dir });
|
|
@@ -17160,33 +17160,33 @@ async function pathExists(path10) {
|
|
|
17160
17160
|
}
|
|
17161
17161
|
}
|
|
17162
17162
|
async function installCodexConfig(apiUrl, token, repoUrl) {
|
|
17163
|
-
const { homedir:
|
|
17164
|
-
const { join:
|
|
17165
|
-
const { chmod:
|
|
17166
|
-
const codexDir =
|
|
17167
|
-
const configPath =
|
|
17168
|
-
const envPath =
|
|
17163
|
+
const { homedir: homedir12 } = await import("node:os");
|
|
17164
|
+
const { join: join18 } = await import("node:path");
|
|
17165
|
+
const { chmod: chmod7, copyFile: copyFile3, mkdir: mkdir11, readFile: readFile11, writeFile: writeFile10 } = await import("node:fs/promises");
|
|
17166
|
+
const codexDir = join18(homedir12(), ".codex");
|
|
17167
|
+
const configPath = join18(codexDir, "config.toml");
|
|
17168
|
+
const envPath = join18(codexDir, ".env");
|
|
17169
17169
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
17170
|
-
await
|
|
17171
|
-
const existingConfig = await pathExists(configPath) ? await
|
|
17170
|
+
await mkdir11(codexDir, { recursive: true });
|
|
17171
|
+
const existingConfig = await pathExists(configPath) ? await readFile11(configPath, "utf8") : "";
|
|
17172
17172
|
if (existingConfig.trim()) {
|
|
17173
17173
|
await copyFile3(configPath, `${configPath}.bak.${timestamp}`);
|
|
17174
17174
|
}
|
|
17175
|
-
await
|
|
17175
|
+
await writeFile10(
|
|
17176
17176
|
configPath,
|
|
17177
17177
|
upsertCodexServerBlock(existingConfig, apiUrl, repoUrl),
|
|
17178
17178
|
"utf8"
|
|
17179
17179
|
);
|
|
17180
|
-
const existingEnv = await pathExists(envPath) ? await
|
|
17180
|
+
const existingEnv = await pathExists(envPath) ? await readFile11(envPath, "utf8") : "";
|
|
17181
17181
|
if (existingEnv.trim()) {
|
|
17182
17182
|
await copyFile3(envPath, `${envPath}.bak.${timestamp}`);
|
|
17183
17183
|
}
|
|
17184
|
-
await
|
|
17184
|
+
await writeFile10(
|
|
17185
17185
|
envPath,
|
|
17186
17186
|
upsertEnvValue(existingEnv, "GREAT_ARROW_TOKEN", token),
|
|
17187
17187
|
"utf8"
|
|
17188
17188
|
);
|
|
17189
|
-
await
|
|
17189
|
+
await chmod7(envPath, 384);
|
|
17190
17190
|
return { configPath, envPath };
|
|
17191
17191
|
}
|
|
17192
17192
|
var startCommand = new Command("start").description(
|
|
@@ -17474,15 +17474,15 @@ var installCommand = new Command("install").description("Generate MCP configurat
|
|
|
17474
17474
|
process.stdout.write(configJson2 + "\n");
|
|
17475
17475
|
return;
|
|
17476
17476
|
}
|
|
17477
|
-
const { homedir:
|
|
17478
|
-
const { join:
|
|
17479
|
-
const { mkdir:
|
|
17480
|
-
const geminiDir =
|
|
17481
|
-
const mcpPath =
|
|
17482
|
-
await
|
|
17477
|
+
const { homedir: homedir12 } = await import("node:os");
|
|
17478
|
+
const { join: join19 } = await import("node:path");
|
|
17479
|
+
const { mkdir: mkdir11, readFile: readFile11, writeFile: writeFile11 } = await import("node:fs/promises");
|
|
17480
|
+
const geminiDir = join19(homedir12(), ".gemini");
|
|
17481
|
+
const mcpPath = join19(geminiDir, "settings.json");
|
|
17482
|
+
await mkdir11(geminiDir, { recursive: true });
|
|
17483
17483
|
let existingConfig = {};
|
|
17484
17484
|
try {
|
|
17485
|
-
const content = await
|
|
17485
|
+
const content = await readFile11(mcpPath, "utf-8");
|
|
17486
17486
|
existingConfig = JSON.parse(content);
|
|
17487
17487
|
} catch {
|
|
17488
17488
|
existingConfig = {};
|
|
@@ -17494,20 +17494,20 @@ var installCommand = new Command("install").description("Generate MCP configurat
|
|
|
17494
17494
|
...config3.mcpServers
|
|
17495
17495
|
}
|
|
17496
17496
|
};
|
|
17497
|
-
await
|
|
17497
|
+
await writeFile11(
|
|
17498
17498
|
mcpPath,
|
|
17499
17499
|
JSON.stringify(existingConfig, null, 2) + "\n",
|
|
17500
17500
|
"utf-8"
|
|
17501
17501
|
);
|
|
17502
17502
|
fmt.success(`Wrote gemini MCP config to ${mcpPath}`);
|
|
17503
|
-
const agyMcpDir =
|
|
17503
|
+
const agyMcpDir = join19(
|
|
17504
17504
|
geminiDir,
|
|
17505
17505
|
"antigravity-cli",
|
|
17506
17506
|
"mcp",
|
|
17507
17507
|
MCP_SERVER_NAME
|
|
17508
17508
|
);
|
|
17509
|
-
await
|
|
17510
|
-
const instructionsPath =
|
|
17509
|
+
await mkdir11(agyMcpDir, { recursive: true });
|
|
17510
|
+
const instructionsPath = join19(agyMcpDir, "instructions.md");
|
|
17511
17511
|
const instructionsContent = `You are connected to greatarrow.ai - the user's private workspace memory + integrations brain.
|
|
17512
17512
|
|
|
17513
17513
|
CRITICAL DIRECTIVE (MANDATORY STANDARD):
|
|
@@ -17523,7 +17523,7 @@ THREE-LAYER LIVE TASK TRACKING:
|
|
|
17523
17523
|
- Maintain client-native harness plans, Great Arrow Kanban cards (\`todo_create\`, \`todo_update\`), and resume states.
|
|
17524
17524
|
|
|
17525
17525
|
Always assume full autonomy to leverage these tools to their maximum extent.`;
|
|
17526
|
-
await
|
|
17526
|
+
await writeFile11(instructionsPath, instructionsContent, "utf-8");
|
|
17527
17527
|
fmt.success(`Wrote gemini MCP instructions to ${instructionsPath}`);
|
|
17528
17528
|
return;
|
|
17529
17529
|
}
|
|
@@ -17538,10 +17538,10 @@ Always assume full autonomy to leverage these tools to their maximum extent.`;
|
|
|
17538
17538
|
process.stdout.write(configJson + "\n");
|
|
17539
17539
|
return;
|
|
17540
17540
|
}
|
|
17541
|
-
const { writeFile:
|
|
17542
|
-
const { join:
|
|
17543
|
-
const outputPath =
|
|
17544
|
-
await
|
|
17541
|
+
const { writeFile: writeFile10 } = await import("node:fs/promises");
|
|
17542
|
+
const { join: join18 } = await import("node:path");
|
|
17543
|
+
const outputPath = join18(process.cwd(), ".mcp.json");
|
|
17544
|
+
await writeFile10(outputPath, configJson + "\n", { mode: 384 });
|
|
17545
17545
|
fmt.success(`Wrote ${clientName} MCP config to .mcp.json`);
|
|
17546
17546
|
if (await mcpJsonAtRiskOfCommit(process.cwd())) {
|
|
17547
17547
|
fmt.warn(
|
|
@@ -17557,9 +17557,9 @@ var KEYCHAIN_SERVICE = "GreatArrowDigital";
|
|
|
17557
17557
|
var KEYCHAIN_ACCOUNT = "api-token";
|
|
17558
17558
|
var OAUTH_CRED_FILENAME = "oauth-credential.json";
|
|
17559
17559
|
async function readIfExists(path10) {
|
|
17560
|
-
const { readFile:
|
|
17560
|
+
const { readFile: readFile11 } = await import("node:fs/promises");
|
|
17561
17561
|
try {
|
|
17562
|
-
return await
|
|
17562
|
+
return await readFile11(path10, "utf8");
|
|
17563
17563
|
} catch {
|
|
17564
17564
|
return null;
|
|
17565
17565
|
}
|
|
@@ -17574,13 +17574,13 @@ async function readKeychainOnly() {
|
|
|
17574
17574
|
}
|
|
17575
17575
|
}
|
|
17576
17576
|
async function resolveProxyScript(override) {
|
|
17577
|
-
const { homedir:
|
|
17578
|
-
const { join:
|
|
17577
|
+
const { homedir: homedir12 } = await import("node:os");
|
|
17578
|
+
const { join: join18 } = await import("node:path");
|
|
17579
17579
|
const candidates = [
|
|
17580
17580
|
override,
|
|
17581
17581
|
process.env.GAD_PROXY_SCRIPT,
|
|
17582
|
-
|
|
17583
|
-
|
|
17582
|
+
join18(homedir12(), ".great-arrow", "mcp-server.mjs"),
|
|
17583
|
+
join18(process.cwd(), "scripts", "mcp-server.mjs")
|
|
17584
17584
|
].filter((c) => Boolean(c));
|
|
17585
17585
|
for (const candidate of candidates) {
|
|
17586
17586
|
if (await pathExists(candidate)) return candidate;
|
|
@@ -17588,9 +17588,9 @@ async function resolveProxyScript(override) {
|
|
|
17588
17588
|
return null;
|
|
17589
17589
|
}
|
|
17590
17590
|
async function runProxyDiagnostics(scriptPath) {
|
|
17591
|
-
const { execFile:
|
|
17591
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
17592
17592
|
const { promisify: promisify2 } = await import("node:util");
|
|
17593
|
-
const run4 = promisify2(
|
|
17593
|
+
const run4 = promisify2(execFile6);
|
|
17594
17594
|
try {
|
|
17595
17595
|
const { stdout } = await run4(
|
|
17596
17596
|
process.execPath,
|
|
@@ -17604,21 +17604,21 @@ async function runProxyDiagnostics(scriptPath) {
|
|
|
17604
17604
|
}
|
|
17605
17605
|
}
|
|
17606
17606
|
async function findLegacyStores(cwd) {
|
|
17607
|
-
const { join:
|
|
17607
|
+
const { join: join18 } = await import("node:path");
|
|
17608
17608
|
const { readdir } = await import("node:fs/promises");
|
|
17609
17609
|
const dirs = /* @__PURE__ */ new Set([cwd]);
|
|
17610
17610
|
try {
|
|
17611
|
-
const worktreeRoot =
|
|
17611
|
+
const worktreeRoot = join18(cwd, ".claude", "worktrees");
|
|
17612
17612
|
for (const entry of await readdir(worktreeRoot, { withFileTypes: true })) {
|
|
17613
|
-
if (entry.isDirectory()) dirs.add(
|
|
17613
|
+
if (entry.isDirectory()) dirs.add(join18(worktreeRoot, entry.name));
|
|
17614
17614
|
}
|
|
17615
17615
|
} catch (err2) {
|
|
17616
17616
|
debugSwallow("mcp.findLegacyStores(worktrees)", err2);
|
|
17617
17617
|
}
|
|
17618
17618
|
try {
|
|
17619
|
-
const { execFile:
|
|
17619
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
17620
17620
|
const { promisify: promisify2 } = await import("node:util");
|
|
17621
|
-
const run4 = promisify2(
|
|
17621
|
+
const run4 = promisify2(execFile6);
|
|
17622
17622
|
const { stdout } = await run4("git", ["worktree", "list", "--porcelain"], {
|
|
17623
17623
|
cwd,
|
|
17624
17624
|
timeout: 5e3
|
|
@@ -17632,18 +17632,18 @@ async function findLegacyStores(cwd) {
|
|
|
17632
17632
|
}
|
|
17633
17633
|
const stores = [];
|
|
17634
17634
|
for (const dir of dirs) {
|
|
17635
|
-
const path10 =
|
|
17635
|
+
const path10 = join18(dir, ".mcp", OAUTH_CRED_FILENAME);
|
|
17636
17636
|
const raw = await readIfExists(path10);
|
|
17637
17637
|
if (raw !== null) stores.push({ path: path10, raw });
|
|
17638
17638
|
}
|
|
17639
17639
|
return stores;
|
|
17640
17640
|
}
|
|
17641
17641
|
async function scanConnections() {
|
|
17642
|
-
const { homedir:
|
|
17643
|
-
const { join:
|
|
17644
|
-
const home =
|
|
17642
|
+
const { homedir: homedir12 } = await import("node:os");
|
|
17643
|
+
const { join: join18 } = await import("node:path");
|
|
17644
|
+
const home = homedir12();
|
|
17645
17645
|
const hookSettings = await readIfExists(
|
|
17646
|
-
|
|
17646
|
+
join18(home, ".claude", "settings.json")
|
|
17647
17647
|
);
|
|
17648
17648
|
const hooksInstalled = hookSettings ? hasGreatArrowHooks(hookSettings) : false;
|
|
17649
17649
|
const entries = [];
|
|
@@ -17676,19 +17676,19 @@ async function probeEndpoint() {
|
|
|
17676
17676
|
}
|
|
17677
17677
|
}
|
|
17678
17678
|
async function applyMigration(plan) {
|
|
17679
|
-
const { homedir:
|
|
17680
|
-
const { join:
|
|
17681
|
-
const { chmod:
|
|
17679
|
+
const { homedir: homedir12 } = await import("node:os");
|
|
17680
|
+
const { join: join18 } = await import("node:path");
|
|
17681
|
+
const { chmod: chmod7, mkdir: mkdir11, rename: rename5, unlink: unlink3, writeFile: writeFile10 } = await import("node:fs/promises");
|
|
17682
17682
|
const applied = [];
|
|
17683
|
-
const dir =
|
|
17684
|
-
await
|
|
17685
|
-
await
|
|
17683
|
+
const dir = join18(homedir12(), ".great-arrow");
|
|
17684
|
+
await mkdir11(dir, { recursive: true, mode: 448 });
|
|
17685
|
+
await chmod7(dir, 448);
|
|
17686
17686
|
const writeAtomic0600 = async (target, raw) => {
|
|
17687
17687
|
const tmp = `${target}.tmp.${process.pid}.${Date.now()}`;
|
|
17688
17688
|
try {
|
|
17689
|
-
await
|
|
17690
|
-
await
|
|
17691
|
-
await
|
|
17689
|
+
await writeFile10(tmp, raw, { mode: 384, flag: "wx" });
|
|
17690
|
+
await chmod7(tmp, 384);
|
|
17691
|
+
await rename5(tmp, target);
|
|
17692
17692
|
} catch (err2) {
|
|
17693
17693
|
try {
|
|
17694
17694
|
await unlink3(tmp);
|
|
@@ -17701,7 +17701,7 @@ async function applyMigration(plan) {
|
|
|
17701
17701
|
if (plan.seedFrom) {
|
|
17702
17702
|
const raw = await readIfExists(plan.seedFrom);
|
|
17703
17703
|
if (raw !== null) {
|
|
17704
|
-
const target =
|
|
17704
|
+
const target = join18(dir, OAUTH_CRED_FILENAME);
|
|
17705
17705
|
await writeAtomic0600(target, raw);
|
|
17706
17706
|
applied.push(`Copied ${plan.seedFrom} \u2192 ${target}`);
|
|
17707
17707
|
}
|
|
@@ -17709,9 +17709,9 @@ async function applyMigration(plan) {
|
|
|
17709
17709
|
if (plan.syncKeychainToFile) {
|
|
17710
17710
|
const raw = await readKeychainOnly();
|
|
17711
17711
|
if (raw) {
|
|
17712
|
-
const target =
|
|
17712
|
+
const target = join18(dir, "credential");
|
|
17713
17713
|
await writeAtomic0600(target, raw);
|
|
17714
|
-
await writeAtomic0600(
|
|
17714
|
+
await writeAtomic0600(join18(dir, "credential.migrated"), "1");
|
|
17715
17715
|
applied.push(`Wrote the keychain credential to ${target}`);
|
|
17716
17716
|
}
|
|
17717
17717
|
}
|
|
@@ -17734,23 +17734,23 @@ var doctorSubcommand = new Command("doctor").description(
|
|
|
17734
17734
|
noColor: false
|
|
17735
17735
|
});
|
|
17736
17736
|
const c = createColor(jsonFlag);
|
|
17737
|
-
const { homedir:
|
|
17738
|
-
const { join:
|
|
17739
|
-
const home =
|
|
17737
|
+
const { homedir: homedir12 } = await import("node:os");
|
|
17738
|
+
const { join: join18 } = await import("node:path");
|
|
17739
|
+
const home = homedir12();
|
|
17740
17740
|
const proxyPath = await resolveProxyScript(options.proxy);
|
|
17741
17741
|
const diagnostics = proxyPath ? await runProxyDiagnostics(proxyPath) : null;
|
|
17742
17742
|
const entries = await scanConnections();
|
|
17743
17743
|
const duplicates = detectDuplicates(entries);
|
|
17744
17744
|
const credentialFile = await readIfExists(
|
|
17745
|
-
|
|
17745
|
+
join18(home, ".great-arrow", "credential")
|
|
17746
17746
|
);
|
|
17747
17747
|
const credentialMigrated = await readIfExists(
|
|
17748
|
-
|
|
17748
|
+
join18(home, ".great-arrow", "credential.migrated")
|
|
17749
17749
|
) !== null;
|
|
17750
17750
|
const fileAuthoritative = credentialFile?.trim() === "LOGGED_OUT" || credentialMigrated || process.platform === "darwin" && credentialFile !== null;
|
|
17751
17751
|
const keychain = fileAuthoritative ? null : await readKeychainOnly();
|
|
17752
17752
|
const userStore = await readIfExists(
|
|
17753
|
-
|
|
17753
|
+
join18(home, ".great-arrow", OAUTH_CRED_FILENAME)
|
|
17754
17754
|
);
|
|
17755
17755
|
const legacyStores = await findLegacyStores(process.cwd());
|
|
17756
17756
|
const findings = [
|
|
@@ -18127,6 +18127,10 @@ function itemLine(item) {
|
|
|
18127
18127
|
const detail = item.detail ? stripControlChars(String(item.detail)) : "";
|
|
18128
18128
|
return detail ? ` ${who} \u2014 ${detail}` : ` ${who}`;
|
|
18129
18129
|
}
|
|
18130
|
+
function liveLines(live) {
|
|
18131
|
+
if (!live) return [];
|
|
18132
|
+
return [live.activity?.line, live.spend?.line].filter((l) => typeof l === "string" && l.length > 0).map((l) => ` ${stripControlChars(l)}`);
|
|
18133
|
+
}
|
|
18130
18134
|
function count(n) {
|
|
18131
18135
|
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
18132
18136
|
}
|
|
@@ -18135,14 +18139,30 @@ function renderAgentsNow(payload) {
|
|
|
18135
18139
|
const groups = (Array.isArray(safe.working) ? safe.working : []).filter(
|
|
18136
18140
|
(g) => g && Array.isArray(g.items) && g.items.length > 0
|
|
18137
18141
|
);
|
|
18142
|
+
const sessions = (Array.isArray(safe.sessions) ? safe.sessions : []).filter(
|
|
18143
|
+
(s2) => s2 && typeof s2.name === "string"
|
|
18144
|
+
);
|
|
18145
|
+
const byId = new Map(
|
|
18146
|
+
(Array.isArray(safe.agents) ? safe.agents : []).map((a) => [a?.id, a])
|
|
18147
|
+
);
|
|
18138
18148
|
const counts = safe.counts ?? {};
|
|
18139
18149
|
const lines = [];
|
|
18140
|
-
if (groups.length === 0) {
|
|
18150
|
+
if (groups.length === 0 && sessions.length === 0) {
|
|
18141
18151
|
lines.push("No agent is working right now.");
|
|
18142
18152
|
} else {
|
|
18143
18153
|
for (const g of groups) {
|
|
18144
18154
|
lines.push(groupTitle(g.where, g.machine));
|
|
18145
|
-
for (const item of g.items)
|
|
18155
|
+
for (const item of g.items) {
|
|
18156
|
+
lines.push(itemLine(item));
|
|
18157
|
+
if (item.connectionId)
|
|
18158
|
+
lines.push(...liveLines(byId.get(item.connectionId)));
|
|
18159
|
+
}
|
|
18160
|
+
lines.push("");
|
|
18161
|
+
}
|
|
18162
|
+
for (const s2 of sessions) {
|
|
18163
|
+
lines.push(groupTitle(s2.where, s2.machine));
|
|
18164
|
+
lines.push(itemLine({ who: s2.name }));
|
|
18165
|
+
lines.push(...liveLines(s2));
|
|
18146
18166
|
lines.push("");
|
|
18147
18167
|
}
|
|
18148
18168
|
if (lines[lines.length - 1] === "") lines.pop();
|
|
@@ -18182,7 +18202,7 @@ function asMessage(body) {
|
|
|
18182
18202
|
return b.status === "message" && typeof b.messageId === "string" && typeof b.block === "string" ? { messageId: b.messageId, block: b.block } : null;
|
|
18183
18203
|
}
|
|
18184
18204
|
async function runInboxWait(o) {
|
|
18185
|
-
const
|
|
18205
|
+
const sleep3 = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18186
18206
|
const now = o.now ?? Date.now;
|
|
18187
18207
|
const write = o.write ?? ((t) => void process.stdout.write(t));
|
|
18188
18208
|
const writeErr = o.writeErr ?? ((t) => void process.stderr.write(t));
|
|
@@ -18212,7 +18232,7 @@ async function runInboxWait(o) {
|
|
|
18212
18232
|
return 0;
|
|
18213
18233
|
}
|
|
18214
18234
|
const gap = Math.min(waitBackoffMs(emptyHolds++), stopAt - now());
|
|
18215
|
-
if (gap > 0) await
|
|
18235
|
+
if (gap > 0) await sleep3(gap);
|
|
18216
18236
|
}
|
|
18217
18237
|
return 3;
|
|
18218
18238
|
}
|
|
@@ -19124,10 +19144,10 @@ async function exclusive(slot, action) {
|
|
|
19124
19144
|
const controller = new AbortController();
|
|
19125
19145
|
let timer;
|
|
19126
19146
|
const operation = Promise.resolve().then(() => action(controller.signal));
|
|
19127
|
-
const
|
|
19147
|
+
const release2 = () => {
|
|
19128
19148
|
if (!controller.signal.aborted) busy.delete(slot);
|
|
19129
19149
|
};
|
|
19130
|
-
void operation.then(
|
|
19150
|
+
void operation.then(release2, release2);
|
|
19131
19151
|
const timeout2 = new Promise((_resolve, reject) => {
|
|
19132
19152
|
timer = setTimeout(() => {
|
|
19133
19153
|
controller.abort();
|
|
@@ -22047,8 +22067,8 @@ function emoji() {
|
|
|
22047
22067
|
}
|
|
22048
22068
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
22049
22069
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
22050
|
-
var mac = (
|
|
22051
|
-
const escapedDelim = escapeRegex(
|
|
22070
|
+
var mac = (delimiter2) => {
|
|
22071
|
+
const escapedDelim = escapeRegex(delimiter2 ?? ":");
|
|
22052
22072
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
22053
22073
|
};
|
|
22054
22074
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -22863,9 +22883,9 @@ var asciiTabOrNewline = /[\t\n\r]/g;
|
|
|
22863
22883
|
function stripTabAndNewline(value) {
|
|
22864
22884
|
return value.replace(asciiTabOrNewline, "");
|
|
22865
22885
|
}
|
|
22866
|
-
function urlHostnameOk(url2,
|
|
22867
|
-
|
|
22868
|
-
return
|
|
22886
|
+
function urlHostnameOk(url2, hostname7) {
|
|
22887
|
+
hostname7.lastIndex = 0;
|
|
22888
|
+
return hostname7.test(url2.hostname);
|
|
22869
22889
|
}
|
|
22870
22890
|
function urlProtocolOk(url2, protocol) {
|
|
22871
22891
|
protocol.lastIndex = 0;
|
|
@@ -25104,7 +25124,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
25104
25124
|
var NONE = 0;
|
|
25105
25125
|
var ASSUMED = 1;
|
|
25106
25126
|
var PROVEN = 2;
|
|
25107
|
-
function isRecursive(inst, stack,
|
|
25127
|
+
function isRecursive(inst, stack, resolve7) {
|
|
25108
25128
|
const cached2 = recursive.get(inst);
|
|
25109
25129
|
if (cached2 !== void 0)
|
|
25110
25130
|
return cached2 ? PROVEN : NONE;
|
|
@@ -25114,7 +25134,7 @@ function isRecursive(inst, stack, resolve5) {
|
|
|
25114
25134
|
let result = NONE;
|
|
25115
25135
|
const check2 = (child) => {
|
|
25116
25136
|
if (result !== PROVEN && child?._zod) {
|
|
25117
|
-
const answer = isRecursive(child, stack,
|
|
25137
|
+
const answer = isRecursive(child, stack, resolve7);
|
|
25118
25138
|
if (answer > result)
|
|
25119
25139
|
result = answer;
|
|
25120
25140
|
}
|
|
@@ -25125,7 +25145,7 @@ function isRecursive(inst, stack, resolve5) {
|
|
|
25125
25145
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
25126
25146
|
if (spread && !desc.enumerable)
|
|
25127
25147
|
continue;
|
|
25128
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
25148
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve7) : NONE;
|
|
25129
25149
|
if (child > answer)
|
|
25130
25150
|
answer = child;
|
|
25131
25151
|
}
|
|
@@ -25189,7 +25209,7 @@ function isRecursive(inst, stack, resolve5) {
|
|
|
25189
25209
|
break;
|
|
25190
25210
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
25191
25211
|
case "lazy": {
|
|
25192
|
-
const inner = def._cachedInner ?? (
|
|
25212
|
+
const inner = def._cachedInner ?? (resolve7 ? inst._zod.innerType : void 0);
|
|
25193
25213
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
25194
25214
|
break;
|
|
25195
25215
|
}
|
|
@@ -43911,34 +43931,34 @@ function validateDownloadUrl(url2) {
|
|
|
43911
43931
|
message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
|
|
43912
43932
|
});
|
|
43913
43933
|
}
|
|
43914
|
-
const
|
|
43915
|
-
if (!
|
|
43934
|
+
const hostname7 = parsed.hostname.toLowerCase().replace(/\.+$/, "");
|
|
43935
|
+
if (!hostname7) {
|
|
43916
43936
|
throw new DownloadError({
|
|
43917
43937
|
url: url2,
|
|
43918
43938
|
message: `URL must have a hostname`
|
|
43919
43939
|
});
|
|
43920
43940
|
}
|
|
43921
|
-
if (
|
|
43941
|
+
if (hostname7 === "localhost" || hostname7.endsWith(".local") || hostname7.endsWith(".localhost") || hostname7.endsWith(".internal")) {
|
|
43922
43942
|
throw new DownloadError({
|
|
43923
43943
|
url: url2,
|
|
43924
|
-
message: `URL with hostname ${
|
|
43944
|
+
message: `URL with hostname ${hostname7} is not allowed`
|
|
43925
43945
|
});
|
|
43926
43946
|
}
|
|
43927
|
-
if (
|
|
43928
|
-
const ipv63 =
|
|
43947
|
+
if (hostname7.startsWith("[") && hostname7.endsWith("]")) {
|
|
43948
|
+
const ipv63 = hostname7.slice(1, -1);
|
|
43929
43949
|
if (isPrivateIPv6(ipv63)) {
|
|
43930
43950
|
throw new DownloadError({
|
|
43931
43951
|
url: url2,
|
|
43932
|
-
message: `URL with IPv6 address ${
|
|
43952
|
+
message: `URL with IPv6 address ${hostname7} is not allowed`
|
|
43933
43953
|
});
|
|
43934
43954
|
}
|
|
43935
43955
|
return;
|
|
43936
43956
|
}
|
|
43937
|
-
if (isIPv4(
|
|
43938
|
-
if (isPrivateIPv4(
|
|
43957
|
+
if (isIPv4(hostname7)) {
|
|
43958
|
+
if (isPrivateIPv4(hostname7)) {
|
|
43939
43959
|
throw new DownloadError({
|
|
43940
43960
|
url: url2,
|
|
43941
|
-
message: `URL with IP address ${
|
|
43961
|
+
message: `URL with IP address ${hostname7} is not allowed`
|
|
43942
43962
|
});
|
|
43943
43963
|
}
|
|
43944
43964
|
}
|
|
@@ -43946,18 +43966,18 @@ function validateDownloadUrl(url2) {
|
|
|
43946
43966
|
function validateDownloadAddress({
|
|
43947
43967
|
address,
|
|
43948
43968
|
family,
|
|
43949
|
-
hostname:
|
|
43969
|
+
hostname: hostname7
|
|
43950
43970
|
}) {
|
|
43951
43971
|
const isUnsafe = family === 4 ? !isIPv4(address) || isPrivateIPv4(address) : family === 6 ? isPrivateIPv6(address) : true;
|
|
43952
43972
|
if (isUnsafe) {
|
|
43953
43973
|
throw new DownloadError({
|
|
43954
|
-
url:
|
|
43955
|
-
message: `Hostname ${
|
|
43974
|
+
url: hostname7,
|
|
43975
|
+
message: `Hostname ${hostname7} resolved to disallowed IP address ${address}`
|
|
43956
43976
|
});
|
|
43957
43977
|
}
|
|
43958
43978
|
}
|
|
43959
|
-
function isIPv4(
|
|
43960
|
-
const parts =
|
|
43979
|
+
function isIPv4(hostname7) {
|
|
43980
|
+
const parts = hostname7.split(".");
|
|
43961
43981
|
if (parts.length !== 4) return false;
|
|
43962
43982
|
return parts.every((part) => {
|
|
43963
43983
|
const num = Number(part);
|
|
@@ -44042,8 +44062,8 @@ function isPrivateIPv6(ip) {
|
|
|
44042
44062
|
return false;
|
|
44043
44063
|
}
|
|
44044
44064
|
function createSafeLookup(lookup) {
|
|
44045
|
-
return ((
|
|
44046
|
-
lookup(
|
|
44065
|
+
return ((hostname7, options, callback) => {
|
|
44066
|
+
lookup(hostname7, { ...options, all: true }, (error62, addresses) => {
|
|
44047
44067
|
if (error62) {
|
|
44048
44068
|
callback(error62);
|
|
44049
44069
|
return;
|
|
@@ -44051,10 +44071,10 @@ function createSafeLookup(lookup) {
|
|
|
44051
44071
|
try {
|
|
44052
44072
|
const [firstAddress] = addresses;
|
|
44053
44073
|
if (firstAddress == null) {
|
|
44054
|
-
throw new Error(`Hostname ${
|
|
44074
|
+
throw new Error(`Hostname ${hostname7} did not resolve to an address`);
|
|
44055
44075
|
}
|
|
44056
44076
|
for (const { address, family } of addresses) {
|
|
44057
|
-
validateDownloadAddress({ address, family, hostname:
|
|
44077
|
+
validateDownloadAddress({ address, family, hostname: hostname7 });
|
|
44058
44078
|
}
|
|
44059
44079
|
if (options.all === true) {
|
|
44060
44080
|
callback(null, addresses);
|
|
@@ -48199,7 +48219,7 @@ async function readCloudConversationReply(options) {
|
|
|
48199
48219
|
let bytes = 0;
|
|
48200
48220
|
let lineBytes = 0;
|
|
48201
48221
|
let released = false;
|
|
48202
|
-
const
|
|
48222
|
+
const release2 = () => {
|
|
48203
48223
|
if (!released) {
|
|
48204
48224
|
released = true;
|
|
48205
48225
|
source.releaseLock();
|
|
@@ -48208,14 +48228,14 @@ async function readCloudConversationReply(options) {
|
|
|
48208
48228
|
const cancel = () => {
|
|
48209
48229
|
if (released) return;
|
|
48210
48230
|
void source.cancel().catch((error62) => logger.warn(error62));
|
|
48211
|
-
|
|
48231
|
+
release2();
|
|
48212
48232
|
};
|
|
48213
48233
|
const bounded3 = new ReadableStream({
|
|
48214
48234
|
async pull(controller) {
|
|
48215
48235
|
try {
|
|
48216
48236
|
const chunk = await abortable(() => source.read(), signal);
|
|
48217
48237
|
if (chunk.done) {
|
|
48218
|
-
|
|
48238
|
+
release2();
|
|
48219
48239
|
controller.close();
|
|
48220
48240
|
return;
|
|
48221
48241
|
}
|
|
@@ -48362,9 +48382,9 @@ function platformFetch(apiUrl, transport, engineFetch) {
|
|
|
48362
48382
|
if (input2 instanceof Request)
|
|
48363
48383
|
throw new Error("Local turn transport requires an explicit request path");
|
|
48364
48384
|
const raw = String(input2);
|
|
48365
|
-
const
|
|
48385
|
+
const relative3 = raw.startsWith("/");
|
|
48366
48386
|
const target = new URL(raw, platform2);
|
|
48367
|
-
if (!
|
|
48387
|
+
if (!relative3 && target.origin !== platform2.origin)
|
|
48368
48388
|
return engineFetch(input2, init);
|
|
48369
48389
|
if (target.origin !== platform2.origin || !target.pathname.startsWith("/api/") || target.username || target.password || target.hash)
|
|
48370
48390
|
throw new Error("Local turn requests must use a platform API path");
|
|
@@ -48835,11 +48855,11 @@ function origin(apiUrl) {
|
|
|
48835
48855
|
function appAwareFetch(apiUrl, token, base, getToken2) {
|
|
48836
48856
|
return async (input2, init) => {
|
|
48837
48857
|
const target = typeof input2 === "string" ? input2 : String(input2);
|
|
48838
|
-
const
|
|
48839
|
-
if (!
|
|
48858
|
+
const relative3 = target.startsWith("/") && !target.startsWith("//");
|
|
48859
|
+
if (!relative3 && !target.startsWith(`${origin(apiUrl)}/`))
|
|
48840
48860
|
return base(input2, init);
|
|
48841
48861
|
const currentToken = getToken2 ? await renewToken(getToken2) : token;
|
|
48842
|
-
return base(
|
|
48862
|
+
return base(relative3 ? `${origin(apiUrl)}${target}` : target, {
|
|
48843
48863
|
...init,
|
|
48844
48864
|
headers: {
|
|
48845
48865
|
...init?.headers,
|
|
@@ -50052,8 +50072,8 @@ function browserLaunchCommand(url2, platform2 = process.platform) {
|
|
|
50052
50072
|
function openBrowser2(url2) {
|
|
50053
50073
|
const command = browserLaunchCommand(url2);
|
|
50054
50074
|
if (!command) return Promise.resolve(false);
|
|
50055
|
-
return new Promise((
|
|
50056
|
-
execFile3(command.file, command.args, (error62) =>
|
|
50075
|
+
return new Promise((resolve7) => {
|
|
50076
|
+
execFile3(command.file, command.args, (error62) => resolve7(!error62));
|
|
50057
50077
|
});
|
|
50058
50078
|
}
|
|
50059
50079
|
function toSurfaceRow(value) {
|
|
@@ -50311,6 +50331,1312 @@ function callCommand() {
|
|
|
50311
50331
|
});
|
|
50312
50332
|
}
|
|
50313
50333
|
|
|
50334
|
+
// src/lib/runner/approval.ts
|
|
50335
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
50336
|
+
import { chmod as chmod4, mkdir as mkdir9, readFile as readFile8, rename as rename3, writeFile as writeFile7 } from "node:fs/promises";
|
|
50337
|
+
import { join as join14 } from "node:path";
|
|
50338
|
+
|
|
50339
|
+
// src/lib/runner/repo.ts
|
|
50340
|
+
import { chmod as chmod3, mkdir as mkdir8, realpath as realpath2, writeFile as writeFile6 } from "node:fs/promises";
|
|
50341
|
+
import { homedir as homedir10 } from "node:os";
|
|
50342
|
+
import { isAbsolute as isAbsolute3, join as join13, relative, resolve as resolve5 } from "node:path";
|
|
50343
|
+
|
|
50344
|
+
// src/lib/runner/git.ts
|
|
50345
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
50346
|
+
var GIT_DEADLINE_MS = 12e4;
|
|
50347
|
+
var REDIRECT_ENV = /* @__PURE__ */ new Set([
|
|
50348
|
+
"GIT_DIR",
|
|
50349
|
+
"GIT_WORK_TREE",
|
|
50350
|
+
"GIT_INDEX_FILE",
|
|
50351
|
+
"GIT_COMMON_DIR",
|
|
50352
|
+
"GIT_OBJECT_DIRECTORY",
|
|
50353
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
50354
|
+
"GIT_CONFIG",
|
|
50355
|
+
"GIT_CONFIG_PARAMETERS",
|
|
50356
|
+
"GIT_CONFIG_COUNT",
|
|
50357
|
+
"GIT_CEILING_DIRECTORIES"
|
|
50358
|
+
]);
|
|
50359
|
+
function gitEnv() {
|
|
50360
|
+
const env4 = {};
|
|
50361
|
+
for (const [name23, value] of Object.entries(process.env)) {
|
|
50362
|
+
if (REDIRECT_ENV.has(name23) || /^GIT_CONFIG_(KEY|VALUE)_/.test(name23))
|
|
50363
|
+
continue;
|
|
50364
|
+
env4[name23] = value;
|
|
50365
|
+
}
|
|
50366
|
+
env4.GIT_TERMINAL_PROMPT = "0";
|
|
50367
|
+
return env4;
|
|
50368
|
+
}
|
|
50369
|
+
function safeGit(cwd, args, opts = {}) {
|
|
50370
|
+
const argv = [
|
|
50371
|
+
"-c",
|
|
50372
|
+
"core.hooksPath=/dev/null",
|
|
50373
|
+
"-c",
|
|
50374
|
+
"core.fsmonitor=false",
|
|
50375
|
+
...args
|
|
50376
|
+
];
|
|
50377
|
+
return new Promise((resolve7) => {
|
|
50378
|
+
execFile4(
|
|
50379
|
+
"git",
|
|
50380
|
+
argv,
|
|
50381
|
+
{
|
|
50382
|
+
cwd,
|
|
50383
|
+
env: gitEnv(),
|
|
50384
|
+
timeout: opts.timeoutMs ?? GIT_DEADLINE_MS,
|
|
50385
|
+
killSignal: "SIGKILL",
|
|
50386
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
50387
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
50388
|
+
},
|
|
50389
|
+
(error62, stdout, stderr) => {
|
|
50390
|
+
const status = error62?.code;
|
|
50391
|
+
const code = !error62 ? 0 : typeof status === "number" ? status : 1;
|
|
50392
|
+
resolve7({ code, stdout: String(stdout), stderr: String(stderr) });
|
|
50393
|
+
}
|
|
50394
|
+
);
|
|
50395
|
+
});
|
|
50396
|
+
}
|
|
50397
|
+
function worktreeGit(layout, args, opts = {}) {
|
|
50398
|
+
return safeGit(
|
|
50399
|
+
layout.worktree,
|
|
50400
|
+
[
|
|
50401
|
+
`--git-dir=${layout.worktreeGitDir}`,
|
|
50402
|
+
`--work-tree=${layout.worktree}`,
|
|
50403
|
+
...args
|
|
50404
|
+
],
|
|
50405
|
+
opts
|
|
50406
|
+
);
|
|
50407
|
+
}
|
|
50408
|
+
|
|
50409
|
+
// src/lib/runner/text.ts
|
|
50410
|
+
function cleanLine(text3) {
|
|
50411
|
+
return text3.replace(/[\u0000-\u001f\u007f-\u009f]+/g, " ").trim();
|
|
50412
|
+
}
|
|
50413
|
+
function cleanText(text3) {
|
|
50414
|
+
return text3.replace(/\r\n?/g, "\n").replace(/\t/g, " ").replace(/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]+/g, " ");
|
|
50415
|
+
}
|
|
50416
|
+
var PREVIEW_LINES = 3;
|
|
50417
|
+
var PREVIEW_CHARS = 240;
|
|
50418
|
+
function preview(text3) {
|
|
50419
|
+
const lines = cleanText(text3).split("\n").map((l) => l.trimEnd()).filter((l) => l.trim() !== "");
|
|
50420
|
+
const out2 = [];
|
|
50421
|
+
let used = 0;
|
|
50422
|
+
for (const line of lines) {
|
|
50423
|
+
if (out2.length === PREVIEW_LINES) {
|
|
50424
|
+
out2[out2.length - 1] = `${out2[out2.length - 1]} \u2026`;
|
|
50425
|
+
break;
|
|
50426
|
+
}
|
|
50427
|
+
const room = PREVIEW_CHARS - used;
|
|
50428
|
+
if (line.length > room) {
|
|
50429
|
+
out2.push(`${line.slice(0, Math.max(room - 1, 0))}\u2026`);
|
|
50430
|
+
break;
|
|
50431
|
+
}
|
|
50432
|
+
out2.push(line);
|
|
50433
|
+
used += line.length;
|
|
50434
|
+
}
|
|
50435
|
+
return out2;
|
|
50436
|
+
}
|
|
50437
|
+
|
|
50438
|
+
// src/lib/runner/sandbox.ts
|
|
50439
|
+
import { lstat, readFile as readFile7, realpath } from "node:fs/promises";
|
|
50440
|
+
import { dirname as dirname8, join as join12, resolve as resolve4 } from "node:path";
|
|
50441
|
+
var TASK_FILE = ".gad/task.md";
|
|
50442
|
+
var CLAUDE_ALLOWED_DOMAINS = [
|
|
50443
|
+
"api.anthropic.com",
|
|
50444
|
+
"www.greatarrow.ai",
|
|
50445
|
+
"registry.npmjs.org",
|
|
50446
|
+
"registry.yarnpkg.com",
|
|
50447
|
+
"pypi.org",
|
|
50448
|
+
"files.pythonhosted.org",
|
|
50449
|
+
"crates.io",
|
|
50450
|
+
"index.crates.io",
|
|
50451
|
+
"static.crates.io"
|
|
50452
|
+
];
|
|
50453
|
+
var DENY_RULES = [
|
|
50454
|
+
"Bash(git push)",
|
|
50455
|
+
"Bash(git push:*)",
|
|
50456
|
+
"Bash(git remote:*)",
|
|
50457
|
+
"Bash(git config:*)",
|
|
50458
|
+
"Bash(gh:*)",
|
|
50459
|
+
"Bash(gh)"
|
|
50460
|
+
];
|
|
50461
|
+
var CREDENTIAL_ENV = [
|
|
50462
|
+
"GH_TOKEN",
|
|
50463
|
+
"GITHUB_TOKEN",
|
|
50464
|
+
"GH_ENTERPRISE_TOKEN",
|
|
50465
|
+
"GITHUB_ENTERPRISE_TOKEN",
|
|
50466
|
+
"GITLAB_TOKEN",
|
|
50467
|
+
"SSH_AUTH_SOCK",
|
|
50468
|
+
"SSH_ASKPASS",
|
|
50469
|
+
"GIT_ASKPASS",
|
|
50470
|
+
"GIT_SSH",
|
|
50471
|
+
"GIT_SSH_COMMAND",
|
|
50472
|
+
"GIT_CONFIG_PARAMETERS",
|
|
50473
|
+
"GIT_CREDENTIAL_HELPER"
|
|
50474
|
+
];
|
|
50475
|
+
var HOME_DENY = [
|
|
50476
|
+
".gitconfig",
|
|
50477
|
+
".config",
|
|
50478
|
+
".ssh",
|
|
50479
|
+
".aws",
|
|
50480
|
+
".gnupg",
|
|
50481
|
+
".zshrc",
|
|
50482
|
+
".zshenv",
|
|
50483
|
+
".zprofile",
|
|
50484
|
+
".zlogin",
|
|
50485
|
+
".bashrc",
|
|
50486
|
+
".bash_profile",
|
|
50487
|
+
".bash_login",
|
|
50488
|
+
".profile",
|
|
50489
|
+
".netrc",
|
|
50490
|
+
".git-credentials",
|
|
50491
|
+
"Library",
|
|
50492
|
+
".docker"
|
|
50493
|
+
];
|
|
50494
|
+
var CLI_CREDENTIAL_ENV = [
|
|
50495
|
+
"GIT_CONFIG_COUNT",
|
|
50496
|
+
"GIT_DIR",
|
|
50497
|
+
"GIT_WORK_TREE",
|
|
50498
|
+
"GIT_INDEX_FILE",
|
|
50499
|
+
"NPM_TOKEN",
|
|
50500
|
+
"NODE_AUTH_TOKEN",
|
|
50501
|
+
"AWS_ACCESS_KEY_ID",
|
|
50502
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
50503
|
+
"AWS_SESSION_TOKEN",
|
|
50504
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
50505
|
+
"VERCEL_TOKEN",
|
|
50506
|
+
"SUPABASE_ACCESS_TOKEN",
|
|
50507
|
+
"OP_SERVICE_ACCOUNT_TOKEN"
|
|
50508
|
+
];
|
|
50509
|
+
var CLI_CREDENTIAL_ENV_PREFIXES = [
|
|
50510
|
+
"GAD_",
|
|
50511
|
+
"GREAT_ARROW_",
|
|
50512
|
+
"GIT_CONFIG_KEY_",
|
|
50513
|
+
"GIT_CONFIG_VALUE_",
|
|
50514
|
+
"AZURE_",
|
|
50515
|
+
"OP_SESSION_"
|
|
50516
|
+
];
|
|
50517
|
+
var CLI_HARDENING = [".git"];
|
|
50518
|
+
var CLI_READ_HARDENING = [
|
|
50519
|
+
".gadrc",
|
|
50520
|
+
".great-arrow",
|
|
50521
|
+
".claude.json",
|
|
50522
|
+
".claude/.credentials.json",
|
|
50523
|
+
".codex/auth.json",
|
|
50524
|
+
".npmrc",
|
|
50525
|
+
".config/configstore",
|
|
50526
|
+
".config/gh",
|
|
50527
|
+
".netrc",
|
|
50528
|
+
".git-credentials",
|
|
50529
|
+
".ssh",
|
|
50530
|
+
".gnupg",
|
|
50531
|
+
".config/gcloud",
|
|
50532
|
+
".aws",
|
|
50533
|
+
".azure",
|
|
50534
|
+
".kube",
|
|
50535
|
+
".docker/config.json",
|
|
50536
|
+
".config/op",
|
|
50537
|
+
".config/1Password",
|
|
50538
|
+
".pypirc",
|
|
50539
|
+
".cargo/credentials.toml",
|
|
50540
|
+
".terraform.d/credentials.tfrc.json"
|
|
50541
|
+
];
|
|
50542
|
+
function approvalFile(layout) {
|
|
50543
|
+
return join12(dirname8(dirname8(layout.worktree)), "approved.json");
|
|
50544
|
+
}
|
|
50545
|
+
function denyRead(layout) {
|
|
50546
|
+
return [
|
|
50547
|
+
...CLI_READ_HARDENING.map((rel) => join12(layout.home, rel)),
|
|
50548
|
+
approvalFile(layout)
|
|
50549
|
+
];
|
|
50550
|
+
}
|
|
50551
|
+
var CREDENTIAL_SET = new Set(
|
|
50552
|
+
[...CREDENTIAL_ENV, ...CLI_CREDENTIAL_ENV].map((n) => n.toLowerCase())
|
|
50553
|
+
);
|
|
50554
|
+
function isCredentialEnv(name23) {
|
|
50555
|
+
const lower = name23.toLowerCase();
|
|
50556
|
+
return CREDENTIAL_SET.has(lower) || CLI_CREDENTIAL_ENV_PREFIXES.some(
|
|
50557
|
+
(prefix) => lower.startsWith(prefix.toLowerCase())
|
|
50558
|
+
);
|
|
50559
|
+
}
|
|
50560
|
+
function stripCredentialEnv(env4) {
|
|
50561
|
+
const out2 = {};
|
|
50562
|
+
for (const [name23, value] of Object.entries(env4)) {
|
|
50563
|
+
if (!isCredentialEnv(name23)) out2[name23] = value;
|
|
50564
|
+
}
|
|
50565
|
+
return out2;
|
|
50566
|
+
}
|
|
50567
|
+
function arrowdeckCommitWrites(layout) {
|
|
50568
|
+
const wt = layout.worktreeGitDir;
|
|
50569
|
+
return [
|
|
50570
|
+
{ path: join12(layout.commonDir, "objects"), dir: true },
|
|
50571
|
+
{ path: join12(layout.commonDir, "refs"), dir: true },
|
|
50572
|
+
{ path: join12(layout.commonDir, "logs"), dir: true },
|
|
50573
|
+
{ path: join12(wt, "HEAD"), dir: false },
|
|
50574
|
+
{ path: join12(wt, "HEAD.lock"), dir: false },
|
|
50575
|
+
{ path: join12(wt, "index"), dir: false },
|
|
50576
|
+
{ path: join12(wt, "index.lock"), dir: false },
|
|
50577
|
+
{ path: join12(wt, "logs"), dir: true },
|
|
50578
|
+
{ path: join12(wt, "ORIG_HEAD"), dir: false },
|
|
50579
|
+
{ path: join12(wt, "COMMIT_EDITMSG"), dir: false },
|
|
50580
|
+
{ path: join12(wt, "AUTO_MERGE"), dir: false }
|
|
50581
|
+
];
|
|
50582
|
+
}
|
|
50583
|
+
function allowWrite(layout) {
|
|
50584
|
+
return [layout.worktree];
|
|
50585
|
+
}
|
|
50586
|
+
function denyWrite(layout) {
|
|
50587
|
+
return [
|
|
50588
|
+
join12(layout.commonDir, "hooks"),
|
|
50589
|
+
join12(layout.commonDir, "config"),
|
|
50590
|
+
join12(layout.commonDir, "info"),
|
|
50591
|
+
join12(layout.commonDir, "modules"),
|
|
50592
|
+
join12(layout.commonDir, "packed-refs"),
|
|
50593
|
+
join12(layout.worktreeGitDir, "config.worktree"),
|
|
50594
|
+
join12(layout.worktreeGitDir, "commondir"),
|
|
50595
|
+
join12(layout.worktreeGitDir, "gitdir"),
|
|
50596
|
+
layout.hooksDir,
|
|
50597
|
+
...HOME_DENY.map((rel) => join12(layout.home, rel)),
|
|
50598
|
+
...CLI_HARDENING.map((rel) => join12(layout.worktree, rel)),
|
|
50599
|
+
...arrowdeckCommitWrites(layout).map((w) => w.path)
|
|
50600
|
+
];
|
|
50601
|
+
}
|
|
50602
|
+
function claudeSettings(layout) {
|
|
50603
|
+
const deny = denyWrite(layout);
|
|
50604
|
+
const unreadable = denyRead(layout);
|
|
50605
|
+
const rules = [...DENY_RULES];
|
|
50606
|
+
for (const path10 of deny) {
|
|
50607
|
+
rules.push(`Edit(/${path10}/**)`);
|
|
50608
|
+
rules.push(`Edit(/${path10})`);
|
|
50609
|
+
}
|
|
50610
|
+
for (const path10 of unreadable) {
|
|
50611
|
+
rules.push(`Read(/${path10}/**)`);
|
|
50612
|
+
rules.push(`Read(/${path10})`);
|
|
50613
|
+
}
|
|
50614
|
+
return JSON.stringify(
|
|
50615
|
+
{
|
|
50616
|
+
permissions: { defaultMode: "acceptEdits", deny: rules },
|
|
50617
|
+
sandbox: {
|
|
50618
|
+
allowUnsandboxedCommands: false,
|
|
50619
|
+
autoAllowBashIfSandboxed: true,
|
|
50620
|
+
enabled: true,
|
|
50621
|
+
failIfUnavailable: true,
|
|
50622
|
+
filesystem: {
|
|
50623
|
+
allowWrite: allowWrite(layout),
|
|
50624
|
+
denyRead: unreadable,
|
|
50625
|
+
denyWrite: deny
|
|
50626
|
+
},
|
|
50627
|
+
network: { allowedDomains: [...CLAUDE_ALLOWED_DOMAINS] }
|
|
50628
|
+
}
|
|
50629
|
+
},
|
|
50630
|
+
null,
|
|
50631
|
+
2
|
|
50632
|
+
);
|
|
50633
|
+
}
|
|
50634
|
+
var CLAUDE_NO_MCP = [
|
|
50635
|
+
"--strict-mcp-config",
|
|
50636
|
+
"--mcp-config",
|
|
50637
|
+
'{"mcpServers":{}}'
|
|
50638
|
+
];
|
|
50639
|
+
function claudeArgs(settingsPath) {
|
|
50640
|
+
return [
|
|
50641
|
+
"--settings",
|
|
50642
|
+
settingsPath,
|
|
50643
|
+
"--permission-mode",
|
|
50644
|
+
"acceptEdits",
|
|
50645
|
+
...CLAUDE_NO_MCP
|
|
50646
|
+
];
|
|
50647
|
+
}
|
|
50648
|
+
var CODEX_PROFILE = "gad_runner";
|
|
50649
|
+
function codexArgs(layout) {
|
|
50650
|
+
const filesystem = [
|
|
50651
|
+
...denyRead(layout).map((p) => `${JSON.stringify(p)}="none"`),
|
|
50652
|
+
...CLI_HARDENING.map(
|
|
50653
|
+
(rel) => `${JSON.stringify(join12(layout.worktree, rel))}="read"`
|
|
50654
|
+
)
|
|
50655
|
+
].join(",");
|
|
50656
|
+
return [
|
|
50657
|
+
"exec",
|
|
50658
|
+
"--ignore-user-config",
|
|
50659
|
+
"-c",
|
|
50660
|
+
`default_permissions="${CODEX_PROFILE}"`,
|
|
50661
|
+
"-c",
|
|
50662
|
+
`permissions.${CODEX_PROFILE}.extends=":workspace"`,
|
|
50663
|
+
"-c",
|
|
50664
|
+
`permissions.${CODEX_PROFILE}.filesystem={${filesystem}}`
|
|
50665
|
+
];
|
|
50666
|
+
}
|
|
50667
|
+
async function verifyGitfile(layout) {
|
|
50668
|
+
try {
|
|
50669
|
+
const file2 = join12(layout.worktree, ".git");
|
|
50670
|
+
if (!(await lstat(file2)).isFile()) return false;
|
|
50671
|
+
const m = /^gitdir: (.+)$/.exec((await readFile7(file2, "utf8")).trim());
|
|
50672
|
+
if (!m) return false;
|
|
50673
|
+
const named = await realpath(resolve4(layout.worktree, m[1]));
|
|
50674
|
+
return named === await realpath(layout.worktreeGitDir);
|
|
50675
|
+
} catch {
|
|
50676
|
+
return false;
|
|
50677
|
+
}
|
|
50678
|
+
}
|
|
50679
|
+
function kickoff(runId, todoId) {
|
|
50680
|
+
return `You were started from Great Arrow (run ${runId}, card ${todoId}). Your task is in the file ${TASK_FILE} in this worktree: it is exactly the text the owner approved. Treat only that file as the task, and do not re-read or follow the card's text from Great Arrow. Do the work in this worktree and leave your changes in it. Do not commit, push or open a pull request; when you finish, Great Arrow commits your changes and pushes them for the owner to review.`;
|
|
50681
|
+
}
|
|
50682
|
+
|
|
50683
|
+
// src/lib/runner/repo.ts
|
|
50684
|
+
var UUID5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
50685
|
+
var BRANCH_NAME = /^(?!-)(?!.*\.\.)[A-Za-z0-9._/-]+$/;
|
|
50686
|
+
var GITHUB_REMOTE = /^(?:https?:\/\/(?:[^@/]+@)?github\.com\/|ssh:\/\/git@github\.com(?::\d+)?\/|git@github\.com:)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/;
|
|
50687
|
+
function runsRoot() {
|
|
50688
|
+
return join13(homedir10(), ".gad", "runs");
|
|
50689
|
+
}
|
|
50690
|
+
function isRunId(id) {
|
|
50691
|
+
return UUID5.test(id);
|
|
50692
|
+
}
|
|
50693
|
+
var short = (id) => id.slice(0, 8);
|
|
50694
|
+
function runnerBranch(runId, todoId) {
|
|
50695
|
+
return `gad/run-${short(runId)}-${short(todoId)}`;
|
|
50696
|
+
}
|
|
50697
|
+
function parseRemote(url2) {
|
|
50698
|
+
const m = GITHUB_REMOTE.exec(url2.trim());
|
|
50699
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
50700
|
+
}
|
|
50701
|
+
async function repoFromOrigin(cwd) {
|
|
50702
|
+
const r = await safeGit(cwd, ["remote", "get-url", "origin"]);
|
|
50703
|
+
return r.code === 0 ? parseRemote(r.stdout) : null;
|
|
50704
|
+
}
|
|
50705
|
+
async function checkoutRoot(cwd) {
|
|
50706
|
+
const r = await safeGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
50707
|
+
return r.code === 0 && r.stdout.trim() ? realpath2(r.stdout.trim()) : null;
|
|
50708
|
+
}
|
|
50709
|
+
async function gitPath(cwd, flag) {
|
|
50710
|
+
const r = await safeGit(cwd, ["rev-parse", flag]);
|
|
50711
|
+
if (r.code !== 0) throw new PrepareError("layout", r.stderr);
|
|
50712
|
+
const out2 = r.stdout.trim();
|
|
50713
|
+
return realpath2(isAbsolute3(out2) ? out2 : resolve5(cwd, out2));
|
|
50714
|
+
}
|
|
50715
|
+
async function privateDir(path10) {
|
|
50716
|
+
await mkdir8(path10, { recursive: true, mode: 448 });
|
|
50717
|
+
await chmod3(path10, 448);
|
|
50718
|
+
}
|
|
50719
|
+
var PrepareError = class extends Error {
|
|
50720
|
+
constructor(step, stderr) {
|
|
50721
|
+
const first = cleanLine(stderr.split("\n").find((l) => l.trim()) ?? "");
|
|
50722
|
+
super(first ? `${step}: ${first.slice(0, 200)}` : step);
|
|
50723
|
+
this.step = step;
|
|
50724
|
+
this.name = "PrepareError";
|
|
50725
|
+
this.gitLine = first.slice(0, 200);
|
|
50726
|
+
}
|
|
50727
|
+
step;
|
|
50728
|
+
gitLine;
|
|
50729
|
+
};
|
|
50730
|
+
function hooksPathSetting(stdout) {
|
|
50731
|
+
const own2 = stdout.split("\n").filter((l) => l.trim() && !l.startsWith("command ")).pop();
|
|
50732
|
+
return own2 ? own2.slice(own2.indexOf(" ") + 1).trim() : null;
|
|
50733
|
+
}
|
|
50734
|
+
async function resolveHooksPath(raw, base) {
|
|
50735
|
+
const path10 = raw.startsWith("~/") ? join13(homedir10(), raw.slice(2)) : raw;
|
|
50736
|
+
const abs = isAbsolute3(path10) ? path10 : resolve5(base, path10);
|
|
50737
|
+
return realpath2(abs).catch(() => abs);
|
|
50738
|
+
}
|
|
50739
|
+
var readHooksPath = (cwd) => safeGit(cwd, ["config", "--show-scope", "--get-all", "core.hooksPath"]);
|
|
50740
|
+
async function configuredHooksDir(worktree, commonDir) {
|
|
50741
|
+
const raw = hooksPathSetting((await readHooksPath(worktree)).stdout);
|
|
50742
|
+
return raw ? resolveHooksPath(raw, worktree) : join13(commonDir, "hooks");
|
|
50743
|
+
}
|
|
50744
|
+
async function repoHooksDir(root) {
|
|
50745
|
+
const raw = hooksPathSetting((await readHooksPath(root)).stdout);
|
|
50746
|
+
if (!raw) return null;
|
|
50747
|
+
const abs = await resolveHooksPath(raw, root);
|
|
50748
|
+
const rel = relative(root, abs);
|
|
50749
|
+
return rel && !rel.startsWith("..") && !isAbsolute3(rel) ? rel : null;
|
|
50750
|
+
}
|
|
50751
|
+
async function prepareWorktree(input2) {
|
|
50752
|
+
const { repoRoot, runId, todoId, base, text: text3 } = input2;
|
|
50753
|
+
if (!isRunId(runId) || !isRunId(todoId))
|
|
50754
|
+
throw new Error("run and card ids must be UUIDs");
|
|
50755
|
+
if (!BRANCH_NAME.test(base)) throw new Error(`bad base branch: ${base}`);
|
|
50756
|
+
const runDir = join13(input2.runsDir ?? runsRoot(), runId);
|
|
50757
|
+
await privateDir(runDir);
|
|
50758
|
+
await privateDir(join13(runDir, "wt"));
|
|
50759
|
+
const path10 = join13(runDir, "wt", short(todoId));
|
|
50760
|
+
const branch = runnerBranch(runId, todoId);
|
|
50761
|
+
const fetch2 = await safeGit(repoRoot, [
|
|
50762
|
+
"fetch",
|
|
50763
|
+
"--no-tags",
|
|
50764
|
+
"origin",
|
|
50765
|
+
`+refs/heads/${base}:refs/remotes/origin/${base}`
|
|
50766
|
+
]);
|
|
50767
|
+
if (fetch2.code !== 0) throw new PrepareError("fetch", fetch2.stderr);
|
|
50768
|
+
const add = await safeGit(repoRoot, [
|
|
50769
|
+
"worktree",
|
|
50770
|
+
"add",
|
|
50771
|
+
"--no-track",
|
|
50772
|
+
"-b",
|
|
50773
|
+
branch,
|
|
50774
|
+
path10,
|
|
50775
|
+
`refs/remotes/origin/${base}`
|
|
50776
|
+
]);
|
|
50777
|
+
if (add.code !== 0) throw new PrepareError("worktree", add.stderr);
|
|
50778
|
+
const worktree = await realpath2(path10);
|
|
50779
|
+
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
50780
|
+
const layout = {
|
|
50781
|
+
worktree,
|
|
50782
|
+
commonDir,
|
|
50783
|
+
worktreeGitDir: await gitPath(worktree, "--absolute-git-dir"),
|
|
50784
|
+
home: homedir10(),
|
|
50785
|
+
hooksDir: await configuredHooksDir(worktree, commonDir),
|
|
50786
|
+
branch
|
|
50787
|
+
};
|
|
50788
|
+
const gad = join13(worktree, ".gad");
|
|
50789
|
+
await privateDir(gad);
|
|
50790
|
+
await writeFile6(join13(gad, ".gitignore"), "*\n", { mode: 384 });
|
|
50791
|
+
const task = join13(worktree, TASK_FILE);
|
|
50792
|
+
await writeFile6(task, text3, { mode: 384 });
|
|
50793
|
+
await chmod3(task, 384);
|
|
50794
|
+
return layout;
|
|
50795
|
+
}
|
|
50796
|
+
|
|
50797
|
+
// src/lib/runner/approval.ts
|
|
50798
|
+
function sha256(text3) {
|
|
50799
|
+
return createHash6("sha256").update(text3, "utf8").digest("hex");
|
|
50800
|
+
}
|
|
50801
|
+
var plainTitle = cleanLine;
|
|
50802
|
+
function approvedFile(runsDir, runId) {
|
|
50803
|
+
if (!isRunId(runId)) throw new Error("run id must be a UUID");
|
|
50804
|
+
return join14(runsDir, runId, "approved.json");
|
|
50805
|
+
}
|
|
50806
|
+
function approveShown(cards) {
|
|
50807
|
+
return new Map(
|
|
50808
|
+
cards.map((c) => [
|
|
50809
|
+
c.todoId,
|
|
50810
|
+
{ title: plainTitle(c.title), text: c.text, sha: sha256(c.text) }
|
|
50811
|
+
])
|
|
50812
|
+
);
|
|
50813
|
+
}
|
|
50814
|
+
async function saveApproved(runsDir, runId, approved) {
|
|
50815
|
+
const file2 = approvedFile(runsDir, runId);
|
|
50816
|
+
const dir = join14(runsDir, runId);
|
|
50817
|
+
await mkdir9(dir, { recursive: true, mode: 448 });
|
|
50818
|
+
await chmod4(runsDir, 448);
|
|
50819
|
+
await chmod4(dir, 448);
|
|
50820
|
+
const body = [...approved].map(([todoId, c]) => ({ todoId, ...c }));
|
|
50821
|
+
const tmp = `${file2}.tmp`;
|
|
50822
|
+
await writeFile7(tmp, JSON.stringify(body, null, 2), { mode: 384 });
|
|
50823
|
+
await chmod4(tmp, 384);
|
|
50824
|
+
await rename3(tmp, file2);
|
|
50825
|
+
}
|
|
50826
|
+
async function loadApproved(runsDir, runId) {
|
|
50827
|
+
let raw;
|
|
50828
|
+
try {
|
|
50829
|
+
raw = JSON.parse(await readFile8(approvedFile(runsDir, runId), "utf8"));
|
|
50830
|
+
} catch {
|
|
50831
|
+
return null;
|
|
50832
|
+
}
|
|
50833
|
+
if (!Array.isArray(raw)) return null;
|
|
50834
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
50835
|
+
for (const entry of raw) {
|
|
50836
|
+
const { todoId, title, text: text3, sha } = entry ?? {};
|
|
50837
|
+
if (typeof todoId !== "string" || typeof title !== "string" || typeof text3 !== "string" || sha !== sha256(text3))
|
|
50838
|
+
continue;
|
|
50839
|
+
out2.set(todoId, { title: plainTitle(title), text: text3, sha });
|
|
50840
|
+
}
|
|
50841
|
+
return out2;
|
|
50842
|
+
}
|
|
50843
|
+
function canResume(saved, runner, now) {
|
|
50844
|
+
if (!saved || !runner || new Date(runner.expiresAt).getTime() <= now)
|
|
50845
|
+
return false;
|
|
50846
|
+
return runner.cards.length > 0 && runner.cards.every((c) => saved.get(c.todoId)?.sha === c.contentSha256);
|
|
50847
|
+
}
|
|
50848
|
+
|
|
50849
|
+
// src/lib/runner/loop.ts
|
|
50850
|
+
var RUNNER_CARD_DEADLINE_MS = 2 * 60 * 6e4;
|
|
50851
|
+
var AGENT_DEADLINE_MS = RUNNER_CARD_DEADLINE_MS - 10 * 6e4;
|
|
50852
|
+
var POLL_MS = 1e4;
|
|
50853
|
+
var HEARTBEAT_MS = 3e4;
|
|
50854
|
+
var CLAIM_TTL_MINUTES = 125;
|
|
50855
|
+
var PUSH_HINT = 'Git could not push from this terminal. Run "gh auth login" (or set up your git credentials) and run the command again.';
|
|
50856
|
+
var FETCH_HINT = 'Git could not fetch from this terminal. Run "gh auth login" (or set up your git credentials) and run the command again.';
|
|
50857
|
+
var AUTH_FAILURE = /authentication|permission denied|could not read (username|password)|terminal prompts disabled|publickey|(?:HTTP|returned error|status)[: ]+(?:code )?\b(?:401|403)\b/i;
|
|
50858
|
+
var DETAIL_CODE = /^(?:[a-z_]+|prepare_failed: (?:fetch|worktree|layout)|agent_exit_(?:\d{1,3}|signal))$/;
|
|
50859
|
+
var RunnerCallError = class extends Error {
|
|
50860
|
+
constructor(code, message) {
|
|
50861
|
+
super(message);
|
|
50862
|
+
this.code = code;
|
|
50863
|
+
this.name = "RunnerCallError";
|
|
50864
|
+
}
|
|
50865
|
+
code;
|
|
50866
|
+
};
|
|
50867
|
+
function asNext(data) {
|
|
50868
|
+
const kind = data.kind;
|
|
50869
|
+
if (kind === "card" && typeof data.todoId === "string")
|
|
50870
|
+
return {
|
|
50871
|
+
kind,
|
|
50872
|
+
todoId: data.todoId,
|
|
50873
|
+
contentSha256: String(data.contentSha256 ?? "")
|
|
50874
|
+
};
|
|
50875
|
+
if (kind === "stop") return { kind, reason: String(data.reason ?? "") };
|
|
50876
|
+
if (kind === "wait" || kind === "done") return { kind };
|
|
50877
|
+
throw new Error("Great Arrow sent an answer this CLI does not understand.");
|
|
50878
|
+
}
|
|
50879
|
+
function describe3(o) {
|
|
50880
|
+
if (o.outcome === "pushed") return "PR opened";
|
|
50881
|
+
if (o.outcome === "no_changes") return "no changes";
|
|
50882
|
+
if (o.outcome === "stopped") return "stopped";
|
|
50883
|
+
if (o.outcome === "card_edited") return "failed: card_edited";
|
|
50884
|
+
return `failed: ${o.detail}`;
|
|
50885
|
+
}
|
|
50886
|
+
async function claimCard(call, todoId) {
|
|
50887
|
+
const claim2 = await call("work_claim", {
|
|
50888
|
+
claim_key: `board-run:${todoId}`,
|
|
50889
|
+
card_id: todoId,
|
|
50890
|
+
ttl_minutes: CLAIM_TTL_MINUTES
|
|
50891
|
+
}).catch(() => null);
|
|
50892
|
+
if (!claim2) return { outcome: "failed", detail: "claim_failed" };
|
|
50893
|
+
return claim2.granted === true ? null : { outcome: "failed", detail: "claimed_elsewhere" };
|
|
50894
|
+
}
|
|
50895
|
+
async function workCard(deps, todoId, card) {
|
|
50896
|
+
let prepared;
|
|
50897
|
+
try {
|
|
50898
|
+
prepared = await deps.prepare(todoId, card.text);
|
|
50899
|
+
} catch (error62) {
|
|
50900
|
+
if (!(error62 instanceof PrepareError))
|
|
50901
|
+
return { outcome: "failed", detail: "prepare_failed" };
|
|
50902
|
+
return {
|
|
50903
|
+
outcome: "failed",
|
|
50904
|
+
detail: `prepare_failed: ${error62.step}`,
|
|
50905
|
+
...error62.gitLine ? { local: error62.gitLine } : {}
|
|
50906
|
+
};
|
|
50907
|
+
}
|
|
50908
|
+
let ran;
|
|
50909
|
+
try {
|
|
50910
|
+
ran = await deps.launch(prepared, todoId, deps.signal);
|
|
50911
|
+
} catch {
|
|
50912
|
+
return { outcome: "failed", detail: "agent_did_not_start" };
|
|
50913
|
+
}
|
|
50914
|
+
if (deps.signal.aborted) return { outcome: "stopped" };
|
|
50915
|
+
if (!ran.groupGone) return { outcome: "failed", detail: "sandbox_tampered" };
|
|
50916
|
+
if (ran.killed) return { outcome: "failed", detail: "timed_out" };
|
|
50917
|
+
if (ran.exit !== 0)
|
|
50918
|
+
return { outcome: "failed", detail: `agent_exit_${ran.exit ?? "signal"}` };
|
|
50919
|
+
const result = await deps.publish(prepared, todoId, card.title, deps.signal).catch(() => ({ failed: "publish_failed" }));
|
|
50920
|
+
if (deps.signal.aborted) return { outcome: "stopped" };
|
|
50921
|
+
if (result === "no_changes") return { outcome: "no_changes" };
|
|
50922
|
+
if ("pushed" in result)
|
|
50923
|
+
return {
|
|
50924
|
+
outcome: "pushed",
|
|
50925
|
+
branch: prepared.branch,
|
|
50926
|
+
headSha: result.pushed
|
|
50927
|
+
};
|
|
50928
|
+
return {
|
|
50929
|
+
outcome: "failed",
|
|
50930
|
+
detail: result.failed,
|
|
50931
|
+
...result.file ? {
|
|
50932
|
+
say: `A possible secret was found in ${cleanLine(result.file)}; nothing was committed or pushed.`
|
|
50933
|
+
} : {}
|
|
50934
|
+
};
|
|
50935
|
+
}
|
|
50936
|
+
function reportFields(o) {
|
|
50937
|
+
if (o.outcome === "pushed")
|
|
50938
|
+
return { outcome: o.outcome, branch: o.branch, head_sha: o.headSha };
|
|
50939
|
+
if (o.outcome === "failed")
|
|
50940
|
+
return {
|
|
50941
|
+
outcome: "failed",
|
|
50942
|
+
detail: DETAIL_CODE.test(o.detail) ? o.detail : "failed"
|
|
50943
|
+
};
|
|
50944
|
+
return { outcome: o.outcome };
|
|
50945
|
+
}
|
|
50946
|
+
async function release(deps, todoId) {
|
|
50947
|
+
try {
|
|
50948
|
+
await deps.call("work_release", { card_id: todoId });
|
|
50949
|
+
} catch {
|
|
50950
|
+
deps.print(
|
|
50951
|
+
`Could not release the claim on card ${short(todoId)}; it lapses on its own.`
|
|
50952
|
+
);
|
|
50953
|
+
}
|
|
50954
|
+
}
|
|
50955
|
+
function heartbeat(deps, runId, runEnded) {
|
|
50956
|
+
let inFlight = false;
|
|
50957
|
+
let missed = 0;
|
|
50958
|
+
const stop2 = deps.every(HEARTBEAT_MS, () => {
|
|
50959
|
+
if (inFlight) return;
|
|
50960
|
+
inFlight = true;
|
|
50961
|
+
deps.call("board_autopilot", { action: "runner_get" }).then(
|
|
50962
|
+
(view) => {
|
|
50963
|
+
missed = 0;
|
|
50964
|
+
if (view.runId !== runId || view.status !== "running") runEnded();
|
|
50965
|
+
},
|
|
50966
|
+
(error62) => {
|
|
50967
|
+
if (error62 instanceof RunnerCallError && error62.code === "no_runner_run")
|
|
50968
|
+
runEnded();
|
|
50969
|
+
else missed += 1;
|
|
50970
|
+
}
|
|
50971
|
+
).finally(() => {
|
|
50972
|
+
inFlight = false;
|
|
50973
|
+
});
|
|
50974
|
+
});
|
|
50975
|
+
return () => {
|
|
50976
|
+
stop2();
|
|
50977
|
+
if (missed > 0)
|
|
50978
|
+
deps.print(
|
|
50979
|
+
`(Great Arrow did not hear from this terminal ${missed} times.)`
|
|
50980
|
+
);
|
|
50981
|
+
};
|
|
50982
|
+
}
|
|
50983
|
+
async function runLoop(deps, input2) {
|
|
50984
|
+
const { runId, approved } = input2;
|
|
50985
|
+
const board = (action, extra = {}) => deps.call("board_autopilot", { action, run_id: runId, ...extra });
|
|
50986
|
+
const total = approved.size;
|
|
50987
|
+
const result = {
|
|
50988
|
+
pushed: 0,
|
|
50989
|
+
noChanges: 0,
|
|
50990
|
+
failed: 0,
|
|
50991
|
+
stopped: false
|
|
50992
|
+
};
|
|
50993
|
+
const numbers = /* @__PURE__ */ new Map();
|
|
50994
|
+
while (!deps.signal.aborted) {
|
|
50995
|
+
const next = asNext(await board("runner_next"));
|
|
50996
|
+
if (next.kind === "done") break;
|
|
50997
|
+
if (next.kind === "stop") {
|
|
50998
|
+
deps.print(`Great Arrow ended the run (${cleanLine(next.reason)}).`);
|
|
50999
|
+
break;
|
|
51000
|
+
}
|
|
51001
|
+
if (next.kind === "wait") {
|
|
51002
|
+
await deps.sleep(POLL_MS, deps.signal);
|
|
51003
|
+
continue;
|
|
51004
|
+
}
|
|
51005
|
+
const card = approved.get(next.todoId);
|
|
51006
|
+
if (card && !numbers.has(next.todoId))
|
|
51007
|
+
numbers.set(next.todoId, numbers.size + 1);
|
|
51008
|
+
const label = card ? `[${numbers.get(next.todoId)}/${total}] ${card.title}` : `[-] card ${short(next.todoId)}`;
|
|
51009
|
+
let claimed = false;
|
|
51010
|
+
let outcome;
|
|
51011
|
+
if (!card) outcome = { outcome: "failed", detail: "not_approved" };
|
|
51012
|
+
else if (next.contentSha256 !== card.sha)
|
|
51013
|
+
outcome = { outcome: "card_edited" };
|
|
51014
|
+
else {
|
|
51015
|
+
const refused = await claimCard(deps.call, next.todoId);
|
|
51016
|
+
claimed = refused === null;
|
|
51017
|
+
if (refused) outcome = refused;
|
|
51018
|
+
else {
|
|
51019
|
+
const cardStop = new AbortController();
|
|
51020
|
+
const abort = () => cardStop.abort();
|
|
51021
|
+
deps.signal.addEventListener("abort", abort, { once: true });
|
|
51022
|
+
const stopBeat = heartbeat(deps, runId, () => {
|
|
51023
|
+
if (!cardStop.signal.aborted)
|
|
51024
|
+
deps.print("The run was stopped on the board. Stopping the agent\u2026");
|
|
51025
|
+
abort();
|
|
51026
|
+
});
|
|
51027
|
+
try {
|
|
51028
|
+
outcome = await workCard(
|
|
51029
|
+
{ ...deps, signal: cardStop.signal },
|
|
51030
|
+
next.todoId,
|
|
51031
|
+
card
|
|
51032
|
+
);
|
|
51033
|
+
} finally {
|
|
51034
|
+
stopBeat();
|
|
51035
|
+
deps.signal.removeEventListener("abort", abort);
|
|
51036
|
+
}
|
|
51037
|
+
}
|
|
51038
|
+
}
|
|
51039
|
+
try {
|
|
51040
|
+
await board("runner_report", {
|
|
51041
|
+
card_id: next.todoId,
|
|
51042
|
+
runner: reportFields(outcome)
|
|
51043
|
+
});
|
|
51044
|
+
} catch (error62) {
|
|
51045
|
+
if (!(error62 instanceof RunnerCallError && error62.code === "card_released"))
|
|
51046
|
+
throw error62;
|
|
51047
|
+
deps.print(`${label} \u2026 taken back on the board`);
|
|
51048
|
+
continue;
|
|
51049
|
+
} finally {
|
|
51050
|
+
if (claimed) await release(deps, next.todoId);
|
|
51051
|
+
}
|
|
51052
|
+
if (outcome.outcome === "stopped") {
|
|
51053
|
+
if (deps.signal.aborted) return { ...result, stopped: true };
|
|
51054
|
+
deps.print(`${label} \u2026 stopped`);
|
|
51055
|
+
continue;
|
|
51056
|
+
}
|
|
51057
|
+
deps.print(`${label} \u2026 ${describe3(outcome)}`);
|
|
51058
|
+
if (outcome.outcome === "failed") {
|
|
51059
|
+
if (outcome.say) deps.print(` ${outcome.say}`);
|
|
51060
|
+
if (outcome.local) {
|
|
51061
|
+
deps.print(` git said: ${outcome.local}`);
|
|
51062
|
+
deps.note(next.todoId, `git said: ${outcome.local}`);
|
|
51063
|
+
}
|
|
51064
|
+
if (outcome.detail === "push_failed") deps.print(PUSH_HINT);
|
|
51065
|
+
if (outcome.detail === "prepare_failed: fetch" && AUTH_FAILURE.test(outcome.local ?? ""))
|
|
51066
|
+
deps.print(FETCH_HINT);
|
|
51067
|
+
}
|
|
51068
|
+
if (outcome.outcome === "pushed") result.pushed += 1;
|
|
51069
|
+
else if (outcome.outcome === "no_changes") result.noChanges += 1;
|
|
51070
|
+
else result.failed += 1;
|
|
51071
|
+
}
|
|
51072
|
+
return { ...result, stopped: deps.signal.aborted };
|
|
51073
|
+
}
|
|
51074
|
+
|
|
51075
|
+
// src/lib/runner/command.ts
|
|
51076
|
+
var STOPPED_MESSAGE = "Stopped. This card is closed for this run; run the same command to go on with the remaining cards.";
|
|
51077
|
+
var AGENT_NAME = {
|
|
51078
|
+
claude: "Claude Code",
|
|
51079
|
+
codex: "Codex"
|
|
51080
|
+
};
|
|
51081
|
+
function agentOf(raw) {
|
|
51082
|
+
const agent = raw ?? "claude";
|
|
51083
|
+
return agent === "claude" || agent === "codex" ? agent : null;
|
|
51084
|
+
}
|
|
51085
|
+
function skippedCount(raw) {
|
|
51086
|
+
const n = raw?.notOwnerAuthored;
|
|
51087
|
+
return typeof n === "number" && Number.isInteger(n) && n > 0 ? n : 0;
|
|
51088
|
+
}
|
|
51089
|
+
function skippedMessage(n) {
|
|
51090
|
+
return `${n} ${n === 1 ? "card was" : "cards were"} skipped: someone else or an agent last edited ${n === 1 ? "it" : "them"}. Open and save a card yourself to run it here.`;
|
|
51091
|
+
}
|
|
51092
|
+
function asView(data) {
|
|
51093
|
+
if (typeof data.runId !== "string" || !isRunId(data.runId)) return null;
|
|
51094
|
+
const cards = Array.isArray(data.cards) ? data.cards : [];
|
|
51095
|
+
return {
|
|
51096
|
+
runId: data.runId,
|
|
51097
|
+
repo: typeof data.repo === "string" ? data.repo : "",
|
|
51098
|
+
base: typeof data.base === "string" ? data.base : "",
|
|
51099
|
+
cards: cards.filter(
|
|
51100
|
+
(c) => !!c && typeof c.todoId === "string" && typeof c.title === "string" && typeof c.text === "string"
|
|
51101
|
+
),
|
|
51102
|
+
runner: data.runner ?? null,
|
|
51103
|
+
skippedNotOwnerAuthored: skippedCount(data.skipped)
|
|
51104
|
+
};
|
|
51105
|
+
}
|
|
51106
|
+
function refusal2(deps, error62) {
|
|
51107
|
+
if (error62 instanceof RunnerCallError)
|
|
51108
|
+
deps.exit(
|
|
51109
|
+
1,
|
|
51110
|
+
cleanLine(error62.message) || `Great Arrow refused (${cleanLine(error62.code)}).`
|
|
51111
|
+
);
|
|
51112
|
+
throw error62;
|
|
51113
|
+
}
|
|
51114
|
+
async function checkFolder(deps, repo) {
|
|
51115
|
+
const { root, origin: origin2 } = await deps.checkout();
|
|
51116
|
+
if (!repo || !root || origin2?.toLowerCase() !== repo.toLowerCase())
|
|
51117
|
+
deps.exit(
|
|
51118
|
+
1,
|
|
51119
|
+
`Run this inside your ${cleanLine(repo) || "code repository"} checkout.`
|
|
51120
|
+
);
|
|
51121
|
+
return root;
|
|
51122
|
+
}
|
|
51123
|
+
async function consent(deps, view, shown, agent, yes) {
|
|
51124
|
+
deps.print("");
|
|
51125
|
+
deps.print(
|
|
51126
|
+
`Clear the board in ${cleanLine(view.repo)}, worked by ${AGENT_NAME[agent]} on this computer:`
|
|
51127
|
+
);
|
|
51128
|
+
[...shown.values()].forEach((c, i) => {
|
|
51129
|
+
deps.print(` ${i + 1}. ${c.title}`);
|
|
51130
|
+
for (const line of preview(c.text)) deps.print(` ${line}`);
|
|
51131
|
+
});
|
|
51132
|
+
deps.print("Approval lasts 24 hours.");
|
|
51133
|
+
deps.print("");
|
|
51134
|
+
if (yes) return;
|
|
51135
|
+
if (!deps.interactive)
|
|
51136
|
+
deps.exit(1, "Run this in a terminal to approve the cards, or pass --yes.");
|
|
51137
|
+
for (; ; ) {
|
|
51138
|
+
const answer = (await deps.ask(
|
|
51139
|
+
`Run these ${shown.size} cards here? [y/N, v to view them in full] `
|
|
51140
|
+
)).trim().toLowerCase();
|
|
51141
|
+
if (answer === "y" || answer === "yes") return;
|
|
51142
|
+
if (answer !== "v") deps.exit(0, "Nothing was run.");
|
|
51143
|
+
[...shown.values()].forEach((c, i) => {
|
|
51144
|
+
deps.print(`--- ${i + 1}. ${c.title} ---`);
|
|
51145
|
+
for (const line of cleanText(c.text).split("\n")) deps.print(line);
|
|
51146
|
+
deps.print("");
|
|
51147
|
+
});
|
|
51148
|
+
}
|
|
51149
|
+
}
|
|
51150
|
+
async function approve(deps, view, agent, yes) {
|
|
51151
|
+
const shown = approveShown(view.cards);
|
|
51152
|
+
await consent(deps, view, shown, agent, yes);
|
|
51153
|
+
let state;
|
|
51154
|
+
try {
|
|
51155
|
+
const data = await deps.call("board_autopilot", {
|
|
51156
|
+
action: "runner_approve",
|
|
51157
|
+
run_id: view.runId,
|
|
51158
|
+
runner: {
|
|
51159
|
+
machine: deps.machine.slice(0, 200),
|
|
51160
|
+
agent,
|
|
51161
|
+
cards: [...shown].map(([todoId, c]) => ({
|
|
51162
|
+
todoId,
|
|
51163
|
+
contentSha256: c.sha
|
|
51164
|
+
}))
|
|
51165
|
+
}
|
|
51166
|
+
});
|
|
51167
|
+
state = data.runner;
|
|
51168
|
+
} catch (error62) {
|
|
51169
|
+
refusal2(deps, error62);
|
|
51170
|
+
}
|
|
51171
|
+
const agreed = new Set(
|
|
51172
|
+
(state?.cards ?? []).map((c) => `${c.todoId}:${c.contentSha256}`)
|
|
51173
|
+
);
|
|
51174
|
+
for (const [todoId, c] of shown)
|
|
51175
|
+
if (!agreed.has(`${todoId}:${c.sha}`)) shown.delete(todoId);
|
|
51176
|
+
if (shown.size === 0)
|
|
51177
|
+
deps.exit(
|
|
51178
|
+
1,
|
|
51179
|
+
"None of the cards shown here are the ones approved for this run. Run the command again to see the current cards."
|
|
51180
|
+
);
|
|
51181
|
+
return shown;
|
|
51182
|
+
}
|
|
51183
|
+
async function runBoardWith(deps, opts) {
|
|
51184
|
+
const agent = agentOf(opts.agent);
|
|
51185
|
+
if (!agent) deps.exit(1, "--agent must be claude or codex.");
|
|
51186
|
+
const check2 = await deps.preflight(agent);
|
|
51187
|
+
if (!check2.ok) deps.exit(1, check2.message);
|
|
51188
|
+
let view = null;
|
|
51189
|
+
try {
|
|
51190
|
+
view = asView(await deps.call("board_autopilot", { action: "runner_get" }));
|
|
51191
|
+
} catch (error62) {
|
|
51192
|
+
refusal2(deps, error62);
|
|
51193
|
+
}
|
|
51194
|
+
if (!view) deps.exit(1, "Great Arrow sent a run this CLI cannot use.");
|
|
51195
|
+
const repoRoot = await checkFolder(deps, view.repo);
|
|
51196
|
+
const hooks = await deps.repoHooksDir(repoRoot);
|
|
51197
|
+
if (hooks)
|
|
51198
|
+
deps.print(
|
|
51199
|
+
`The agent can't change files in ${cleanLine(hooks)} (git hooks) on this computer.`
|
|
51200
|
+
);
|
|
51201
|
+
const saved = await loadApproved(deps.runsDir, view.runId);
|
|
51202
|
+
let approved;
|
|
51203
|
+
if (saved && canResume(saved, view.runner, deps.now())) {
|
|
51204
|
+
approved = saved;
|
|
51205
|
+
deps.print(
|
|
51206
|
+
`Picking up run ${short(view.runId)} where it left off (${saved.size} cards approved).`
|
|
51207
|
+
);
|
|
51208
|
+
} else {
|
|
51209
|
+
if (view.skippedNotOwnerAuthored > 0)
|
|
51210
|
+
deps.print(skippedMessage(view.skippedNotOwnerAuthored));
|
|
51211
|
+
if (view.cards.length === 0)
|
|
51212
|
+
deps.exit(
|
|
51213
|
+
0,
|
|
51214
|
+
view.skippedNotOwnerAuthored > 0 ? "Nothing was run." : "No cards are ready to run."
|
|
51215
|
+
);
|
|
51216
|
+
approved = await approve(deps, view, agent, opts.yes === true);
|
|
51217
|
+
await saveApproved(deps.runsDir, view.runId, approved);
|
|
51218
|
+
}
|
|
51219
|
+
const controller = new AbortController();
|
|
51220
|
+
let group = null;
|
|
51221
|
+
deps.onInterrupt(() => {
|
|
51222
|
+
if (controller.signal.aborted) {
|
|
51223
|
+
if (group !== null) deps.killGroup(group);
|
|
51224
|
+
deps.exit(130, STOPPED_MESSAGE);
|
|
51225
|
+
}
|
|
51226
|
+
deps.print("Stopping the agent\u2026");
|
|
51227
|
+
controller.abort();
|
|
51228
|
+
});
|
|
51229
|
+
const work2 = deps.work({
|
|
51230
|
+
view,
|
|
51231
|
+
repoRoot,
|
|
51232
|
+
agent,
|
|
51233
|
+
bin: check2.bin,
|
|
51234
|
+
onSpawn: (pgid) => {
|
|
51235
|
+
group = pgid;
|
|
51236
|
+
},
|
|
51237
|
+
onReaped: () => {
|
|
51238
|
+
group = null;
|
|
51239
|
+
}
|
|
51240
|
+
});
|
|
51241
|
+
let result;
|
|
51242
|
+
try {
|
|
51243
|
+
result = await runLoop(
|
|
51244
|
+
{
|
|
51245
|
+
...work2,
|
|
51246
|
+
call: deps.call,
|
|
51247
|
+
signal: controller.signal,
|
|
51248
|
+
print: deps.print
|
|
51249
|
+
},
|
|
51250
|
+
{ runId: view.runId, approved }
|
|
51251
|
+
);
|
|
51252
|
+
} catch (error62) {
|
|
51253
|
+
refusal2(deps, error62);
|
|
51254
|
+
}
|
|
51255
|
+
if (result.stopped) deps.exit(130, STOPPED_MESSAGE);
|
|
51256
|
+
deps.print("");
|
|
51257
|
+
deps.print(
|
|
51258
|
+
`Finished: ${result.pushed} PRs opened, ${result.noChanges} with no changes, ${result.failed} not done. Logs are in ${deps.runsDir}/${view.runId}.`
|
|
51259
|
+
);
|
|
51260
|
+
}
|
|
51261
|
+
|
|
51262
|
+
// src/commands/board-run.ts
|
|
51263
|
+
import { appendFileSync } from "node:fs";
|
|
51264
|
+
import { chmod as chmod5, writeFile as writeFile8 } from "node:fs/promises";
|
|
51265
|
+
import { hostname as hostname5 } from "node:os";
|
|
51266
|
+
import { join as join16 } from "node:path";
|
|
51267
|
+
import { createInterface as createInterface3 } from "node:readline";
|
|
51268
|
+
|
|
51269
|
+
// src/lib/runner/agent.ts
|
|
51270
|
+
import { spawn } from "node:child_process";
|
|
51271
|
+
import { accessSync, chmodSync as chmodSync3, closeSync, constants as constants2, openSync } from "node:fs";
|
|
51272
|
+
import { delimiter, join as join15 } from "node:path";
|
|
51273
|
+
var KILL_GRACE_MS = 5e3;
|
|
51274
|
+
var INSTALL_MESSAGE = {
|
|
51275
|
+
claude: "Install Claude Code (https://claude.com/claude-code), then run the command again.",
|
|
51276
|
+
codex: "Install Codex (npm install -g @openai/codex), then run the command again.",
|
|
51277
|
+
bwrap: "Install bubblewrap (for example: sudo apt install bubblewrap), then run the command again."
|
|
51278
|
+
};
|
|
51279
|
+
function onPath(name23, env4) {
|
|
51280
|
+
for (const dir of (env4.PATH ?? "").split(delimiter)) {
|
|
51281
|
+
if (!dir) continue;
|
|
51282
|
+
const candidate = join15(dir, name23);
|
|
51283
|
+
try {
|
|
51284
|
+
accessSync(candidate, constants2.X_OK);
|
|
51285
|
+
return candidate;
|
|
51286
|
+
} catch {
|
|
51287
|
+
continue;
|
|
51288
|
+
}
|
|
51289
|
+
}
|
|
51290
|
+
return null;
|
|
51291
|
+
}
|
|
51292
|
+
async function preflightAgent(agent, platform2, env4 = process.env) {
|
|
51293
|
+
if (platform2 === "win32") {
|
|
51294
|
+
return {
|
|
51295
|
+
ok: false,
|
|
51296
|
+
message: "Clear the board runs on macOS, Linux or WSL."
|
|
51297
|
+
};
|
|
51298
|
+
}
|
|
51299
|
+
const bin = onPath(agent, env4);
|
|
51300
|
+
if (!bin) return { ok: false, message: INSTALL_MESSAGE[agent] };
|
|
51301
|
+
if (platform2 === "linux" && !onPath("bwrap", env4)) {
|
|
51302
|
+
return { ok: false, message: INSTALL_MESSAGE.bwrap };
|
|
51303
|
+
}
|
|
51304
|
+
return { ok: true, bin };
|
|
51305
|
+
}
|
|
51306
|
+
function agentArgv(input2) {
|
|
51307
|
+
return input2.agent === "claude" ? [...claudeArgs(input2.settingsPath), "-p", input2.kickoff] : [...codexArgs(input2.layout), input2.kickoff];
|
|
51308
|
+
}
|
|
51309
|
+
async function runAgent(input2) {
|
|
51310
|
+
if (input2.signal.aborted) return { exit: null, killed: true, pgid: null };
|
|
51311
|
+
const fd = openSync(input2.logPath, "a", 384);
|
|
51312
|
+
chmodSync3(input2.logPath, 384);
|
|
51313
|
+
const env4 = { ...stripCredentialEnv(process.env), GIT_TERMINAL_PROMPT: "0" };
|
|
51314
|
+
let child;
|
|
51315
|
+
try {
|
|
51316
|
+
child = spawn(input2.bin, agentArgv(input2), {
|
|
51317
|
+
cwd: input2.layout.worktree,
|
|
51318
|
+
detached: true,
|
|
51319
|
+
stdio: ["ignore", fd, fd],
|
|
51320
|
+
env: env4
|
|
51321
|
+
});
|
|
51322
|
+
} finally {
|
|
51323
|
+
closeSync(fd);
|
|
51324
|
+
}
|
|
51325
|
+
const pid = child.pid;
|
|
51326
|
+
if (pid !== void 0) input2.onSpawn?.(pid);
|
|
51327
|
+
return new Promise((resolve7, reject) => {
|
|
51328
|
+
let killed = false;
|
|
51329
|
+
let killTimer;
|
|
51330
|
+
const signalGroup = (sig) => {
|
|
51331
|
+
if (pid === void 0) return;
|
|
51332
|
+
try {
|
|
51333
|
+
process.kill(-pid, sig);
|
|
51334
|
+
} catch {
|
|
51335
|
+
return;
|
|
51336
|
+
}
|
|
51337
|
+
};
|
|
51338
|
+
const terminate = () => {
|
|
51339
|
+
if (killed) return;
|
|
51340
|
+
killed = true;
|
|
51341
|
+
signalGroup("SIGTERM");
|
|
51342
|
+
killTimer = setTimeout(() => signalGroup("SIGKILL"), KILL_GRACE_MS);
|
|
51343
|
+
};
|
|
51344
|
+
const deadline = setTimeout(terminate, input2.deadlineMs);
|
|
51345
|
+
input2.signal.addEventListener("abort", terminate, { once: true });
|
|
51346
|
+
const cleanup = () => {
|
|
51347
|
+
clearTimeout(deadline);
|
|
51348
|
+
if (killTimer) clearTimeout(killTimer);
|
|
51349
|
+
input2.signal.removeEventListener("abort", terminate);
|
|
51350
|
+
};
|
|
51351
|
+
child.once("error", (err2) => {
|
|
51352
|
+
cleanup();
|
|
51353
|
+
reject(err2);
|
|
51354
|
+
});
|
|
51355
|
+
child.once("close", (code) => {
|
|
51356
|
+
cleanup();
|
|
51357
|
+
if (killed) signalGroup("SIGKILL");
|
|
51358
|
+
resolve7({ exit: code, killed, pgid: pid ?? null });
|
|
51359
|
+
});
|
|
51360
|
+
});
|
|
51361
|
+
}
|
|
51362
|
+
var REAP_WAIT_MS = 2e3;
|
|
51363
|
+
function groupAlive(pgid) {
|
|
51364
|
+
try {
|
|
51365
|
+
process.kill(-pgid, 0);
|
|
51366
|
+
return true;
|
|
51367
|
+
} catch (error62) {
|
|
51368
|
+
return error62.code !== "ESRCH";
|
|
51369
|
+
}
|
|
51370
|
+
}
|
|
51371
|
+
async function reapGroup(pgid, waitMs = REAP_WAIT_MS) {
|
|
51372
|
+
try {
|
|
51373
|
+
process.kill(-pgid, "SIGKILL");
|
|
51374
|
+
} catch (error62) {
|
|
51375
|
+
if (error62.code === "ESRCH") return true;
|
|
51376
|
+
}
|
|
51377
|
+
const until = Date.now() + waitMs;
|
|
51378
|
+
while (groupAlive(pgid)) {
|
|
51379
|
+
if (Date.now() >= until) return false;
|
|
51380
|
+
await new Promise((resolve7) => setTimeout(resolve7, 25));
|
|
51381
|
+
}
|
|
51382
|
+
return true;
|
|
51383
|
+
}
|
|
51384
|
+
|
|
51385
|
+
// src/lib/runner/secrets.ts
|
|
51386
|
+
var SECRET_PATTERNS = [
|
|
51387
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
51388
|
+
/gh[pousr]_[A-Za-z0-9]{36,}/,
|
|
51389
|
+
/github_pat_[A-Za-z0-9_]{60,}/,
|
|
51390
|
+
/sk_live_[A-Za-z0-9]{20,}/,
|
|
51391
|
+
/AKIA[0-9A-Z]{16}/,
|
|
51392
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/,
|
|
51393
|
+
/gad_v1_[A-Za-z0-9_-]{20,}/
|
|
51394
|
+
];
|
|
51395
|
+
function fileWithSecret(diff) {
|
|
51396
|
+
let file2 = null;
|
|
51397
|
+
for (const line of diff.split("\n")) {
|
|
51398
|
+
if (line.startsWith("+++ ")) {
|
|
51399
|
+
file2 = line.startsWith("+++ b/") ? line.slice(6) : line.slice(4);
|
|
51400
|
+
continue;
|
|
51401
|
+
}
|
|
51402
|
+
if (!line.startsWith("+")) continue;
|
|
51403
|
+
if (SECRET_PATTERNS.some((p) => p.test(line))) return file2 ?? "a file";
|
|
51404
|
+
}
|
|
51405
|
+
return null;
|
|
51406
|
+
}
|
|
51407
|
+
|
|
51408
|
+
// src/lib/runner/publish.ts
|
|
51409
|
+
var RUNNER_BRANCH = /^gad\/run-[0-9a-f]{8}-[0-9a-f]{8}$/;
|
|
51410
|
+
function commitMessage(title, runId, todoId) {
|
|
51411
|
+
const subject = title.replace(/\s+/g, " ").trim() || "Clear the board card";
|
|
51412
|
+
return `${subject}
|
|
51413
|
+
|
|
51414
|
+
Clear the board run ${short(runId)}, card ${short(todoId)}`;
|
|
51415
|
+
}
|
|
51416
|
+
async function publishBranch(layout, branch, base, message, signal) {
|
|
51417
|
+
if (!await verifyGitfile(layout)) return { failed: "sandbox_tampered" };
|
|
51418
|
+
if (!RUNNER_BRANCH.test(branch) || branch === base)
|
|
51419
|
+
return { failed: "bad_branch" };
|
|
51420
|
+
const git2 = (args) => worktreeGit(layout, args, signal ? { signal } : {});
|
|
51421
|
+
if ((await git2(["add", "-A"])).code !== 0) return { failed: "commit_failed" };
|
|
51422
|
+
if ((await git2(["reset", "-q", "--", ".gad"])).code !== 0)
|
|
51423
|
+
return { failed: "commit_failed" };
|
|
51424
|
+
const diff = await git2([
|
|
51425
|
+
"diff",
|
|
51426
|
+
"--cached",
|
|
51427
|
+
"-U0",
|
|
51428
|
+
"--no-color",
|
|
51429
|
+
"--no-ext-diff",
|
|
51430
|
+
"--no-textconv"
|
|
51431
|
+
]);
|
|
51432
|
+
if (diff.code !== 0) return { failed: "commit_failed" };
|
|
51433
|
+
const leaked = fileWithSecret(diff.stdout);
|
|
51434
|
+
if (leaked) return { failed: "secret_in_diff", file: leaked };
|
|
51435
|
+
const staged = await git2(["diff", "--cached", "--quiet"]);
|
|
51436
|
+
if (staged.code > 1) return { failed: "commit_failed" };
|
|
51437
|
+
if (staged.code === 1 && (await git2(["commit", "-q", "-m", message])).code !== 0)
|
|
51438
|
+
return { failed: "commit_failed" };
|
|
51439
|
+
const ahead = await git2([
|
|
51440
|
+
"rev-list",
|
|
51441
|
+
"--count",
|
|
51442
|
+
`refs/remotes/origin/${base}..HEAD`
|
|
51443
|
+
]);
|
|
51444
|
+
if (ahead.code !== 0) return { failed: "commit_failed" };
|
|
51445
|
+
if (Number(ahead.stdout.trim()) === 0) return "no_changes";
|
|
51446
|
+
const task = await git2([
|
|
51447
|
+
"ls-tree",
|
|
51448
|
+
"-r",
|
|
51449
|
+
"--name-only",
|
|
51450
|
+
"HEAD",
|
|
51451
|
+
"--",
|
|
51452
|
+
".gad"
|
|
51453
|
+
]);
|
|
51454
|
+
if (task.code !== 0 || task.stdout.trim() !== "")
|
|
51455
|
+
return { failed: "task_file_committed" };
|
|
51456
|
+
const head = await git2(["rev-parse", "HEAD"]);
|
|
51457
|
+
const sha = head.stdout.trim();
|
|
51458
|
+
if (head.code !== 0 || !/^[0-9a-f]{40}$/.test(sha))
|
|
51459
|
+
return { failed: "commit_failed" };
|
|
51460
|
+
const push = await git2(["push", "origin", `${sha}:refs/heads/${branch}`]);
|
|
51461
|
+
return push.code === 0 ? { pushed: sha } : { failed: "push_failed" };
|
|
51462
|
+
}
|
|
51463
|
+
|
|
51464
|
+
// src/commands/board-run.ts
|
|
51465
|
+
function parseData(result) {
|
|
51466
|
+
if (result && typeof result === "object" && !Array.isArray(result))
|
|
51467
|
+
return result;
|
|
51468
|
+
if (typeof result !== "string") return {};
|
|
51469
|
+
const tail = result.slice(result.lastIndexOf("\n") + 1).trim();
|
|
51470
|
+
try {
|
|
51471
|
+
const data = JSON.parse(tail);
|
|
51472
|
+
return data && typeof data === "object" ? data : {};
|
|
51473
|
+
} catch {
|
|
51474
|
+
return {};
|
|
51475
|
+
}
|
|
51476
|
+
}
|
|
51477
|
+
function makeCall(client2) {
|
|
51478
|
+
return async (tool2, args) => {
|
|
51479
|
+
const heartbeat2 = args.action === "runner_get";
|
|
51480
|
+
let result;
|
|
51481
|
+
try {
|
|
51482
|
+
result = tool2 === "board_autopilot" ? await client2.mcpCall(
|
|
51483
|
+
"gad_call",
|
|
51484
|
+
gadCallArgs(tool2, args),
|
|
51485
|
+
heartbeat2 ? 2e4 : 6e4
|
|
51486
|
+
) : await client2.mcpCall(tool2, args, 3e4);
|
|
51487
|
+
} catch (error62) {
|
|
51488
|
+
if (error62 instanceof McpToolError)
|
|
51489
|
+
throw new RunnerCallError(error62.toolCode, error62.message);
|
|
51490
|
+
throw error62;
|
|
51491
|
+
}
|
|
51492
|
+
const failure2 = callFailure(result);
|
|
51493
|
+
if (failure2) throw new RunnerCallError(failure2.code, failure2.message);
|
|
51494
|
+
return parseData(result);
|
|
51495
|
+
};
|
|
51496
|
+
}
|
|
51497
|
+
function exit(code, message) {
|
|
51498
|
+
if (message)
|
|
51499
|
+
(code === 1 ? process.stderr : process.stdout).write(`${message}
|
|
51500
|
+
`);
|
|
51501
|
+
process.exit(code);
|
|
51502
|
+
}
|
|
51503
|
+
function ask2(question) {
|
|
51504
|
+
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
51505
|
+
return new Promise(
|
|
51506
|
+
(resolve7) => rl.question(question, (answer) => {
|
|
51507
|
+
rl.close();
|
|
51508
|
+
resolve7(answer);
|
|
51509
|
+
})
|
|
51510
|
+
);
|
|
51511
|
+
}
|
|
51512
|
+
function sleep2(ms, signal) {
|
|
51513
|
+
return new Promise((resolve7) => {
|
|
51514
|
+
if (signal.aborted) return resolve7();
|
|
51515
|
+
const timer = setTimeout(done, ms);
|
|
51516
|
+
function done() {
|
|
51517
|
+
clearTimeout(timer);
|
|
51518
|
+
signal.removeEventListener("abort", done);
|
|
51519
|
+
resolve7();
|
|
51520
|
+
}
|
|
51521
|
+
signal.addEventListener("abort", done, { once: true });
|
|
51522
|
+
});
|
|
51523
|
+
}
|
|
51524
|
+
var work = ({
|
|
51525
|
+
view,
|
|
51526
|
+
repoRoot,
|
|
51527
|
+
agent,
|
|
51528
|
+
bin,
|
|
51529
|
+
onSpawn,
|
|
51530
|
+
onReaped
|
|
51531
|
+
}) => {
|
|
51532
|
+
const runsDir = runsRoot();
|
|
51533
|
+
const runDir = join16(runsDir, view.runId);
|
|
51534
|
+
const deps = {
|
|
51535
|
+
prepare: (todoId, text3) => prepareWorktree({
|
|
51536
|
+
repoRoot,
|
|
51537
|
+
runId: view.runId,
|
|
51538
|
+
todoId,
|
|
51539
|
+
base: view.base,
|
|
51540
|
+
text: text3,
|
|
51541
|
+
runsDir
|
|
51542
|
+
}),
|
|
51543
|
+
launch: async (prepared, todoId, signal) => {
|
|
51544
|
+
const settingsPath = join16(runDir, `${short(todoId)}.settings.json`);
|
|
51545
|
+
await writeFile8(settingsPath, claudeSettings(prepared), { mode: 384 });
|
|
51546
|
+
await chmod5(settingsPath, 384);
|
|
51547
|
+
const ran = await runAgent({
|
|
51548
|
+
agent,
|
|
51549
|
+
bin,
|
|
51550
|
+
layout: prepared,
|
|
51551
|
+
settingsPath,
|
|
51552
|
+
kickoff: kickoff(view.runId, todoId),
|
|
51553
|
+
logPath: join16(runDir, `${short(todoId)}.log`),
|
|
51554
|
+
deadlineMs: AGENT_DEADLINE_MS,
|
|
51555
|
+
signal,
|
|
51556
|
+
onSpawn
|
|
51557
|
+
});
|
|
51558
|
+
const groupGone = ran.pgid === null ? true : await reapGroup(ran.pgid);
|
|
51559
|
+
if (groupGone) onReaped();
|
|
51560
|
+
return { exit: ran.exit, killed: ran.killed, groupGone };
|
|
51561
|
+
},
|
|
51562
|
+
publish: (prepared, todoId, title, signal) => publishBranch(
|
|
51563
|
+
prepared,
|
|
51564
|
+
prepared.branch,
|
|
51565
|
+
view.base,
|
|
51566
|
+
commitMessage(title, view.runId, todoId),
|
|
51567
|
+
signal
|
|
51568
|
+
),
|
|
51569
|
+
sleep: sleep2,
|
|
51570
|
+
note: (todoId, line) => {
|
|
51571
|
+
try {
|
|
51572
|
+
appendFileSync(join16(runDir, `${short(todoId)}.log`), `${line}
|
|
51573
|
+
`, {
|
|
51574
|
+
mode: 384
|
|
51575
|
+
});
|
|
51576
|
+
} catch (error62) {
|
|
51577
|
+
process.stderr.write(
|
|
51578
|
+
`Could not write the card's log: ${error62.message}
|
|
51579
|
+
`
|
|
51580
|
+
);
|
|
51581
|
+
}
|
|
51582
|
+
},
|
|
51583
|
+
every: (ms, tick) => {
|
|
51584
|
+
const timer = setInterval(tick, ms);
|
|
51585
|
+
return () => clearInterval(timer);
|
|
51586
|
+
}
|
|
51587
|
+
};
|
|
51588
|
+
return deps;
|
|
51589
|
+
};
|
|
51590
|
+
async function commandDeps() {
|
|
51591
|
+
const auth = await resolveAuth();
|
|
51592
|
+
const call = makeCall(
|
|
51593
|
+
new GadClient({
|
|
51594
|
+
token: auth.token,
|
|
51595
|
+
getToken: auth.getToken,
|
|
51596
|
+
apiUrl: auth.apiUrl
|
|
51597
|
+
})
|
|
51598
|
+
);
|
|
51599
|
+
return {
|
|
51600
|
+
preflight: (agent) => preflightAgent(agent, process.platform),
|
|
51601
|
+
checkout: async () => {
|
|
51602
|
+
const cwd = process.cwd();
|
|
51603
|
+
const [root, origin2] = await Promise.all([
|
|
51604
|
+
checkoutRoot(cwd),
|
|
51605
|
+
repoFromOrigin(cwd)
|
|
51606
|
+
]);
|
|
51607
|
+
return { root, origin: origin2 };
|
|
51608
|
+
},
|
|
51609
|
+
repoHooksDir,
|
|
51610
|
+
call,
|
|
51611
|
+
ask: ask2,
|
|
51612
|
+
interactive: process.stdin.isTTY === true && process.stdout.isTTY === true,
|
|
51613
|
+
print: (line) => process.stdout.write(`${line}
|
|
51614
|
+
`),
|
|
51615
|
+
exit,
|
|
51616
|
+
now: () => Date.now(),
|
|
51617
|
+
runsDir: runsRoot(),
|
|
51618
|
+
machine: hostname5(),
|
|
51619
|
+
onInterrupt: (handler) => {
|
|
51620
|
+
process.on("SIGINT", handler);
|
|
51621
|
+
process.on("SIGTERM", handler);
|
|
51622
|
+
},
|
|
51623
|
+
killGroup: (pgid) => {
|
|
51624
|
+
try {
|
|
51625
|
+
process.kill(-pgid, "SIGKILL");
|
|
51626
|
+
} catch (error62) {
|
|
51627
|
+
if (error62.code !== "ESRCH") throw error62;
|
|
51628
|
+
}
|
|
51629
|
+
},
|
|
51630
|
+
work
|
|
51631
|
+
};
|
|
51632
|
+
}
|
|
51633
|
+
async function runBoard(opts) {
|
|
51634
|
+
await runBoardWith(await commandDeps(), opts);
|
|
51635
|
+
}
|
|
51636
|
+
function boardRunCommand() {
|
|
51637
|
+
return new Command("run").description("Work the cards of a Clear the board run on this computer").option("--agent <name>", "claude or codex (default: claude)").option("--yes", "Approve the listed cards without asking").action((opts) => runBoard(opts));
|
|
51638
|
+
}
|
|
51639
|
+
|
|
50314
51640
|
// src/commands/board.ts
|
|
50315
51641
|
var TOOL = "board_autopilot";
|
|
50316
51642
|
function asObject4(json3) {
|
|
@@ -50491,7 +51817,16 @@ function emit(result, json3, quiet = false) {
|
|
|
50491
51817
|
return data;
|
|
50492
51818
|
}
|
|
50493
51819
|
async function runClear(opts, command) {
|
|
50494
|
-
|
|
51820
|
+
const here = opts.here === true;
|
|
51821
|
+
let runner = null;
|
|
51822
|
+
if (here) {
|
|
51823
|
+
const agent = agentOf(opts.agent);
|
|
51824
|
+
if (!agent) throw validation("--agent must be claude or codex.");
|
|
51825
|
+
runner = await commandDeps();
|
|
51826
|
+
const check2 = await runner.preflight(agent);
|
|
51827
|
+
if (!check2.ok) runner.exit(1, check2.message);
|
|
51828
|
+
}
|
|
51829
|
+
if (opts.yes || here)
|
|
50495
51830
|
buildStartArgs(
|
|
50496
51831
|
"pending",
|
|
50497
51832
|
{ quotes: [{ executor: "hosted", p90Usd: 1 }] },
|
|
@@ -50503,10 +51838,10 @@ async function runClear(opts, command) {
|
|
|
50503
51838
|
action: "plan",
|
|
50504
51839
|
...opts.goal ? { goal_id: opts.goal } : {}
|
|
50505
51840
|
});
|
|
50506
|
-
const data = emit(planResult, g.json && !opts.yes, g.quiet);
|
|
51841
|
+
const data = emit(planResult, g.json && !opts.yes && !here, g.quiet);
|
|
50507
51842
|
const run4 = data?.run ?? null;
|
|
50508
51843
|
if (!g.json && !g.quiet && run4?.plan) printPlan(fmt, run4.plan);
|
|
50509
|
-
if (!opts.yes) {
|
|
51844
|
+
if (!opts.yes && !here) {
|
|
50510
51845
|
if (!g.json && !g.quiet && run4?.id)
|
|
50511
51846
|
process.stdout.write(
|
|
50512
51847
|
`
|
|
@@ -50516,11 +51851,18 @@ Nothing has changed. Start with: gad board clear${opts.goal ? ` --goal ${opts.go
|
|
|
50516
51851
|
return;
|
|
50517
51852
|
}
|
|
50518
51853
|
if (!run4?.id) throw validation("The plan reply carried no run id.");
|
|
50519
|
-
|
|
50520
|
-
|
|
50521
|
-
|
|
50522
|
-
|
|
51854
|
+
if (runner) {
|
|
51855
|
+
await checkFolder(runner, run4.plan?.repo ?? "");
|
|
51856
|
+
process.stdout.write(
|
|
51857
|
+
opts.yes ? "Starting the run on Great Arrow.\n" : "Starting the run on Great Arrow; nothing runs on this computer until you type y.\n"
|
|
51858
|
+
);
|
|
51859
|
+
}
|
|
51860
|
+
const startResult = await callBoard(client2, {
|
|
51861
|
+
...buildStartArgs(run4.id, run4.plan, opts),
|
|
51862
|
+
...here ? { executor: "runner" } : {}
|
|
51863
|
+
});
|
|
50523
51864
|
emit(startResult, g.json, g.quiet);
|
|
51865
|
+
if (runner) await runBoardWith(runner, { agent: opts.agent, yes: opts.yes });
|
|
50524
51866
|
}
|
|
50525
51867
|
function actionCommand(action) {
|
|
50526
51868
|
return new Command(action).description(`${action} a clear-the-board run`).option("--run <id>", "Run id (status defaults to the latest run)").option("--card <id>", "Undo only this card (undo)").action(async (opts, command) => {
|
|
@@ -50545,47 +51887,47 @@ function boardCommand() {
|
|
|
50545
51887
|
).option("--cap <usd>", "Spend cap in USD (default: hosted p90 quote)").option("--types <list>", "Comma-separated card types, e.g. code").option("--no-email", "Do not email when the run finishes").option("--no-push", "Do not push-notify when the run finishes").option(
|
|
50546
51888
|
"--reject <findingId...>",
|
|
50547
51889
|
"Finding ids to leave out of the accepted set"
|
|
50548
|
-
).option("--json", "Output the raw tool result");
|
|
51890
|
+
).option("--here", "Start the run and work its cards in this terminal").option("--agent <name>", "With --here: claude or codex (default: claude)").option("--json", "Output the raw tool result");
|
|
50549
51891
|
clear.action(runClear);
|
|
50550
51892
|
clear.addCommand(actionCommand("status"));
|
|
50551
51893
|
clear.addCommand(actionCommand("stop"));
|
|
50552
51894
|
clear.addCommand(actionCommand("undo"));
|
|
50553
|
-
return new Command("board").description("Clear the board: tidy cards and run the safe ones").addCommand(clear);
|
|
51895
|
+
return new Command("board").description("Clear the board: tidy cards and run the safe ones").addCommand(clear).addCommand(boardRunCommand());
|
|
50554
51896
|
}
|
|
50555
51897
|
|
|
50556
51898
|
// src/commands/keys.ts
|
|
50557
|
-
import { createInterface as
|
|
51899
|
+
import { createInterface as createInterface4 } from "node:readline";
|
|
50558
51900
|
import { Writable } from "node:stream";
|
|
50559
51901
|
|
|
50560
51902
|
// src/lib/keys-apply.ts
|
|
50561
|
-
import { spawn } from "node:child_process";
|
|
51903
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
50562
51904
|
import {
|
|
50563
|
-
chmod as
|
|
50564
|
-
mkdir as
|
|
50565
|
-
readFile as
|
|
50566
|
-
realpath,
|
|
50567
|
-
rename as
|
|
51905
|
+
chmod as chmod6,
|
|
51906
|
+
mkdir as mkdir10,
|
|
51907
|
+
readFile as readFile9,
|
|
51908
|
+
realpath as realpath3,
|
|
51909
|
+
rename as rename4,
|
|
50568
51910
|
stat as stat2,
|
|
50569
|
-
writeFile as
|
|
51911
|
+
writeFile as writeFile9
|
|
50570
51912
|
} from "node:fs/promises";
|
|
50571
|
-
import { homedir as
|
|
50572
|
-
import { basename as basename2, dirname as
|
|
51913
|
+
import { homedir as homedir11 } from "node:os";
|
|
51914
|
+
import { basename as basename2, dirname as dirname9, isAbsolute as isAbsolute4, join as join17, relative as relative2, sep } from "node:path";
|
|
50573
51915
|
var GH_APPLY_TIMEOUT_MS = 3e4;
|
|
50574
51916
|
var GIT_APPLY_TIMEOUT_MS = 3e4;
|
|
50575
51917
|
function runWithStdin(file2, args, input2, timeoutMs) {
|
|
50576
|
-
return new Promise((
|
|
51918
|
+
return new Promise((resolve7) => {
|
|
50577
51919
|
let settled = false;
|
|
50578
51920
|
const done = (o) => {
|
|
50579
51921
|
if (settled) return;
|
|
50580
51922
|
settled = true;
|
|
50581
51923
|
clearTimeout(timer);
|
|
50582
|
-
|
|
51924
|
+
resolve7(o);
|
|
50583
51925
|
};
|
|
50584
51926
|
let child;
|
|
50585
51927
|
try {
|
|
50586
|
-
child =
|
|
51928
|
+
child = spawn2(file2, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
50587
51929
|
} catch {
|
|
50588
|
-
|
|
51930
|
+
resolve7("not_found");
|
|
50589
51931
|
return;
|
|
50590
51932
|
}
|
|
50591
51933
|
const timer = setTimeout(() => {
|
|
@@ -50639,8 +51981,8 @@ password=${value}
|
|
|
50639
51981
|
return { ok: false, code: `git_${approved}` };
|
|
50640
51982
|
}
|
|
50641
51983
|
function expandHome(path10) {
|
|
50642
|
-
if (path10 === "~") return
|
|
50643
|
-
if (path10.startsWith("~/")) return
|
|
51984
|
+
if (path10 === "~") return homedir11();
|
|
51985
|
+
if (path10.startsWith("~/")) return join17(homedir11(), path10.slice(2));
|
|
50644
51986
|
return path10;
|
|
50645
51987
|
}
|
|
50646
51988
|
function escapeRegExp(s2) {
|
|
@@ -50669,16 +52011,16 @@ function formatEnvValue(value, quote) {
|
|
|
50669
52011
|
}
|
|
50670
52012
|
async function writeAtomic(path10, content, mode) {
|
|
50671
52013
|
const tmp = `${path10}.tmp`;
|
|
50672
|
-
await
|
|
50673
|
-
await
|
|
50674
|
-
await
|
|
52014
|
+
await writeFile9(tmp, content, { mode });
|
|
52015
|
+
await chmod6(tmp, mode);
|
|
52016
|
+
await rename4(tmp, path10);
|
|
50675
52017
|
}
|
|
50676
52018
|
async function applyEnvFile(path10, key, value) {
|
|
50677
52019
|
const file2 = expandHome(path10);
|
|
50678
52020
|
let original;
|
|
50679
52021
|
let mode = 384;
|
|
50680
52022
|
try {
|
|
50681
|
-
original = await
|
|
52023
|
+
original = await readFile9(file2, "utf8");
|
|
50682
52024
|
mode = (await stat2(file2)).mode & 511;
|
|
50683
52025
|
} catch (err2) {
|
|
50684
52026
|
if (err2?.code !== "ENOENT") {
|
|
@@ -50726,7 +52068,7 @@ async function applySshKey(path10, privatePem) {
|
|
|
50726
52068
|
const content = privatePem.endsWith("\n") ? privatePem : `${privatePem}
|
|
50727
52069
|
`;
|
|
50728
52070
|
try {
|
|
50729
|
-
await
|
|
52071
|
+
await mkdir10(dirname9(file2), { recursive: true, mode: 448 });
|
|
50730
52072
|
await writeAtomic(file2, content, 384);
|
|
50731
52073
|
} catch {
|
|
50732
52074
|
return { ok: false, code: "ssh_write_failed" };
|
|
@@ -50734,7 +52076,7 @@ async function applySshKey(path10, privatePem) {
|
|
|
50734
52076
|
return { ok: true, code: "applied" };
|
|
50735
52077
|
}
|
|
50736
52078
|
function envTargetRefusal(path10, key, reported) {
|
|
50737
|
-
if (!
|
|
52079
|
+
if (!isAbsolute4(path10)) return { ok: false, code: "target_invalid" };
|
|
50738
52080
|
const known = reported.some((r) => r.path === path10 && r.key === key);
|
|
50739
52081
|
return known ? null : { ok: false, code: "target_not_reported" };
|
|
50740
52082
|
}
|
|
@@ -50744,22 +52086,22 @@ var SSH_NOT_A_KEY = /* @__PURE__ */ new Set([
|
|
|
50744
52086
|
"config",
|
|
50745
52087
|
"known_hosts"
|
|
50746
52088
|
]);
|
|
50747
|
-
async function sshTargetRefusal(path10, sshDir =
|
|
52089
|
+
async function sshTargetRefusal(path10, sshDir = join17(homedir11(), ".ssh")) {
|
|
50748
52090
|
const file2 = expandHome(path10);
|
|
50749
|
-
if (!
|
|
52091
|
+
if (!isAbsolute4(file2)) return { ok: false, code: "target_invalid" };
|
|
50750
52092
|
if (SSH_NOT_A_KEY.has(basename2(file2))) {
|
|
50751
52093
|
return { ok: false, code: "target_not_a_key" };
|
|
50752
52094
|
}
|
|
50753
52095
|
let root;
|
|
50754
52096
|
let parent;
|
|
50755
52097
|
try {
|
|
50756
|
-
root = await
|
|
50757
|
-
parent = await
|
|
52098
|
+
root = await realpath3(sshDir);
|
|
52099
|
+
parent = await realpath3(dirname9(file2));
|
|
50758
52100
|
} catch {
|
|
50759
52101
|
return { ok: false, code: "target_outside_ssh_dir" };
|
|
50760
52102
|
}
|
|
50761
|
-
const rel =
|
|
50762
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) ||
|
|
52103
|
+
const rel = relative2(root, parent);
|
|
52104
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute4(rel)) {
|
|
50763
52105
|
return { ok: false, code: "target_outside_ssh_dir" };
|
|
50764
52106
|
}
|
|
50765
52107
|
return null;
|
|
@@ -50798,10 +52140,10 @@ async function applyUpdate(targetKind, targetRef, value, scope) {
|
|
|
50798
52140
|
}
|
|
50799
52141
|
|
|
50800
52142
|
// src/lib/keys-device.ts
|
|
50801
|
-
import { execFile as
|
|
50802
|
-
import { readFile as
|
|
50803
|
-
import { hostname as
|
|
50804
|
-
import { resolve as
|
|
52143
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
52144
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
52145
|
+
import { hostname as hostname6 } from "node:os";
|
|
52146
|
+
import { resolve as resolve6 } from "node:path";
|
|
50805
52147
|
|
|
50806
52148
|
// src/lib/keys-envelope.ts
|
|
50807
52149
|
import {
|
|
@@ -50902,7 +52244,7 @@ function openFromServer(sealed, devicePrivateKeyB64) {
|
|
|
50902
52244
|
}
|
|
50903
52245
|
|
|
50904
52246
|
// src/lib/keys-handoff.ts
|
|
50905
|
-
import { spawn as
|
|
52247
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
50906
52248
|
|
|
50907
52249
|
// src/lib/keys-paths.ts
|
|
50908
52250
|
function keyRotationPath() {
|
|
@@ -50922,18 +52264,18 @@ var BROWSER_LAUNCH_TIMEOUT_MS = 5e3;
|
|
|
50922
52264
|
function openInBrowser(url2) {
|
|
50923
52265
|
const command = browserLaunchCommand(url2);
|
|
50924
52266
|
if (!command) return Promise.resolve(false);
|
|
50925
|
-
return new Promise((
|
|
52267
|
+
return new Promise((resolve7) => {
|
|
50926
52268
|
let settled = false;
|
|
50927
52269
|
const done = (ok) => {
|
|
50928
52270
|
if (settled) return;
|
|
50929
52271
|
settled = true;
|
|
50930
52272
|
clearTimeout(timer);
|
|
50931
|
-
|
|
52273
|
+
resolve7(ok);
|
|
50932
52274
|
};
|
|
50933
52275
|
const timer = setTimeout(() => done(false), BROWSER_LAUNCH_TIMEOUT_MS);
|
|
50934
52276
|
timer.unref?.();
|
|
50935
52277
|
try {
|
|
50936
|
-
const child =
|
|
52278
|
+
const child = spawn3(command.file, command.args, {
|
|
50937
52279
|
detached: true,
|
|
50938
52280
|
stdio: "ignore"
|
|
50939
52281
|
});
|
|
@@ -50947,7 +52289,7 @@ function openInBrowser(url2) {
|
|
|
50947
52289
|
}
|
|
50948
52290
|
});
|
|
50949
52291
|
}
|
|
50950
|
-
var defaultSleep = (ms) => new Promise((
|
|
52292
|
+
var defaultSleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
50951
52293
|
function stepUpError(message, hint) {
|
|
50952
52294
|
return new CliError(message, 1 /* AUTH_ERROR */, hint);
|
|
50953
52295
|
}
|
|
@@ -50955,7 +52297,7 @@ async function requestBrowserStepUp(client2, kind, subjectId, opts = {}) {
|
|
|
50955
52297
|
const openUrl = opts.openUrl ?? openInBrowser;
|
|
50956
52298
|
const notify = opts.notify ?? ((line) => process.stderr.write(`${line}
|
|
50957
52299
|
`));
|
|
50958
|
-
const
|
|
52300
|
+
const sleep3 = opts.sleep ?? defaultSleep;
|
|
50959
52301
|
const now = opts.now ?? Date.now;
|
|
50960
52302
|
const interval = opts.pollIntervalMs ?? HANDOFF_POLL_INTERVAL_MS;
|
|
50961
52303
|
const deadline = now() + (opts.deadlineMs ?? HANDOFF_DEADLINE_MS);
|
|
@@ -50998,7 +52340,7 @@ async function requestBrowserStepUp(client2, kind, subjectId, opts = {}) {
|
|
|
50998
52340
|
"Run the command again and approve within 5 minutes."
|
|
50999
52341
|
);
|
|
51000
52342
|
}
|
|
51001
|
-
await
|
|
52343
|
+
await sleep3(interval);
|
|
51002
52344
|
}
|
|
51003
52345
|
throw stepUpError(
|
|
51004
52346
|
"Timed out waiting for the browser approval.",
|
|
@@ -51014,7 +52356,7 @@ var REPORT_KEY = /^[A-Z_][A-Z0-9_]{0,127}$/;
|
|
|
51014
52356
|
var REPORT_PATH = /^[ -~]{1,200}$/;
|
|
51015
52357
|
var TOKEN_SHAPED = /[A-Za-z0-9]{33,}/;
|
|
51016
52358
|
var SECRET_PREFIX = /(gh[pousr]_|github_pat_|-----BEGIN)/;
|
|
51017
|
-
var
|
|
52359
|
+
var UUID6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
51018
52360
|
function deviceSlot(workspaceId) {
|
|
51019
52361
|
return `key-rotation-device:${workspaceId}`;
|
|
51020
52362
|
}
|
|
@@ -51091,13 +52433,13 @@ async function scanEnvKeys(paths) {
|
|
|
51091
52433
|
const out2 = [];
|
|
51092
52434
|
const seen = /* @__PURE__ */ new Set();
|
|
51093
52435
|
for (const p of paths) {
|
|
51094
|
-
const path10 =
|
|
52436
|
+
const path10 = resolve6(p);
|
|
51095
52437
|
if (seen.has(path10)) continue;
|
|
51096
52438
|
seen.add(path10);
|
|
51097
52439
|
if (!REPORT_PATH.test(path10) || !nameOk(path10)) continue;
|
|
51098
52440
|
let text3;
|
|
51099
52441
|
try {
|
|
51100
|
-
text3 = await
|
|
52442
|
+
text3 = await readFile10(path10, "utf8");
|
|
51101
52443
|
} catch {
|
|
51102
52444
|
continue;
|
|
51103
52445
|
}
|
|
@@ -51123,7 +52465,7 @@ function helperKind(raw) {
|
|
|
51123
52465
|
}
|
|
51124
52466
|
function run3(file2, args) {
|
|
51125
52467
|
return new Promise((done) => {
|
|
51126
|
-
|
|
52468
|
+
execFile5(
|
|
51127
52469
|
file2,
|
|
51128
52470
|
args,
|
|
51129
52471
|
{ timeout: DETECT_TIMEOUT_MS, encoding: "utf8" },
|
|
@@ -51152,7 +52494,7 @@ async function buildDeviceReport(envFiles) {
|
|
|
51152
52494
|
return { envKeys, ghCli, gitHelper };
|
|
51153
52495
|
}
|
|
51154
52496
|
function defaultDeviceLabel() {
|
|
51155
|
-
const label =
|
|
52497
|
+
const label = hostname6().replace(/[^ -~]/g, "-").slice(0, 80).trim();
|
|
51156
52498
|
return label && nameOk(label) ? label : "this machine";
|
|
51157
52499
|
}
|
|
51158
52500
|
async function enroll(client2, workspaceId, opts = {}) {
|
|
@@ -51192,7 +52534,7 @@ async function enroll(client2, workspaceId, opts = {}) {
|
|
|
51192
52534
|
publicKey: pair.publicKey,
|
|
51193
52535
|
report
|
|
51194
52536
|
});
|
|
51195
|
-
const enrollmentId = typeof begun?.enrollmentId === "string" &&
|
|
52537
|
+
const enrollmentId = typeof begun?.enrollmentId === "string" && UUID6.test(begun.enrollmentId) ? begun.enrollmentId : null;
|
|
51196
52538
|
if (!enrollmentId) {
|
|
51197
52539
|
throw new CliError(
|
|
51198
52540
|
"The server did not start the enrollment.",
|
|
@@ -51210,7 +52552,7 @@ async function enroll(client2, workspaceId, opts = {}) {
|
|
|
51210
52552
|
action: "enroll",
|
|
51211
52553
|
approvalToken
|
|
51212
52554
|
});
|
|
51213
|
-
const deviceId = typeof done?.deviceId === "string" &&
|
|
52555
|
+
const deviceId = typeof done?.deviceId === "string" && UUID6.test(done.deviceId) ? done.deviceId : null;
|
|
51214
52556
|
if (!deviceId) {
|
|
51215
52557
|
throw new CliError(
|
|
51216
52558
|
"The server did not complete the enrollment.",
|
|
@@ -51239,7 +52581,7 @@ async function enroll(client2, workspaceId, opts = {}) {
|
|
|
51239
52581
|
}
|
|
51240
52582
|
|
|
51241
52583
|
// src/commands/keys.ts
|
|
51242
|
-
var
|
|
52584
|
+
var UUID7 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
51243
52585
|
function patRequired(apiUrl) {
|
|
51244
52586
|
return authError(
|
|
51245
52587
|
"gad keys needs a personal access token; this sign-in is an OAuth session.",
|
|
@@ -51292,7 +52634,7 @@ async function keysContext() {
|
|
|
51292
52634
|
});
|
|
51293
52635
|
const value = await probe.mcpCall("workspace_introspect", {}, 5e3);
|
|
51294
52636
|
const workspaceId = value?.workspace?.id;
|
|
51295
|
-
if (value?.error || !workspaceId || !
|
|
52637
|
+
if (value?.error || !workspaceId || !UUID7.test(workspaceId)) {
|
|
51296
52638
|
throw validationError(
|
|
51297
52639
|
"GreatArrow.ai could not confirm your workspace.",
|
|
51298
52640
|
"Run 'gad whoami', or choose one with 'gad --workspace <id>'."
|
|
@@ -51313,7 +52655,7 @@ async function keysContext() {
|
|
|
51313
52655
|
};
|
|
51314
52656
|
}
|
|
51315
52657
|
function requireRunId(runId) {
|
|
51316
|
-
if (!
|
|
52658
|
+
if (!UUID7.test(runId)) {
|
|
51317
52659
|
throw validationError(
|
|
51318
52660
|
`'${stripControlChars(runId)}' is not a run id.`,
|
|
51319
52661
|
"Copy the run id from 'gad keys list' or the run page."
|
|
@@ -51442,7 +52784,7 @@ function enrollCommand() {
|
|
|
51442
52784
|
function toUpdateRow(v) {
|
|
51443
52785
|
if (typeof v !== "object" || v === null) return null;
|
|
51444
52786
|
const r = v;
|
|
51445
|
-
if (typeof r.id !== "string" || !
|
|
52787
|
+
if (typeof r.id !== "string" || !UUID7.test(r.id)) return null;
|
|
51446
52788
|
const ref = {};
|
|
51447
52789
|
if (typeof r.targetRef === "object" && r.targetRef !== null) {
|
|
51448
52790
|
for (const [k, val] of Object.entries(r.targetRef)) {
|
|
@@ -51625,20 +52967,20 @@ function readHiddenFromTty(prompt, io = {
|
|
|
51625
52967
|
callback();
|
|
51626
52968
|
}
|
|
51627
52969
|
});
|
|
51628
|
-
const rl =
|
|
52970
|
+
const rl = createInterface4({
|
|
51629
52971
|
input: io.input,
|
|
51630
52972
|
output: sink,
|
|
51631
52973
|
terminal: true
|
|
51632
52974
|
});
|
|
51633
52975
|
io.output.write(prompt);
|
|
51634
52976
|
muted = true;
|
|
51635
|
-
return new Promise((
|
|
52977
|
+
return new Promise((resolve7, reject) => {
|
|
51636
52978
|
const lines = [];
|
|
51637
52979
|
let pem = false;
|
|
51638
52980
|
const finish = () => {
|
|
51639
52981
|
rl.close();
|
|
51640
52982
|
io.output.write("\n");
|
|
51641
|
-
|
|
52983
|
+
resolve7(lines.join("\n"));
|
|
51642
52984
|
};
|
|
51643
52985
|
rl.on("line", (line) => {
|
|
51644
52986
|
if (lines.length === 0) pem = /^-----BEGIN /.test(line.trim());
|
|
@@ -51994,7 +53336,7 @@ function completionsCommand() {
|
|
|
51994
53336
|
|
|
51995
53337
|
// node_modules/.pnpm/update-notifier@7.3.1/node_modules/update-notifier/update-notifier.js
|
|
51996
53338
|
import process11 from "node:process";
|
|
51997
|
-
import { spawn as
|
|
53339
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
51998
53340
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
51999
53341
|
import path9 from "node:path";
|
|
52000
53342
|
import { format } from "node:util";
|
|
@@ -52070,7 +53412,7 @@ var retryifyAsync = (fn, options) => {
|
|
|
52070
53412
|
throw error62;
|
|
52071
53413
|
const delay2 = Math.round(interval * Math.random());
|
|
52072
53414
|
if (delay2 > 0) {
|
|
52073
|
-
const delayPromise = new Promise((
|
|
53415
|
+
const delayPromise = new Promise((resolve7) => setTimeout(resolve7, delay2));
|
|
52074
53416
|
return delayPromise.then(() => attempt.apply(void 0, args));
|
|
52075
53417
|
} else {
|
|
52076
53418
|
return attempt.apply(void 0, args);
|
|
@@ -53636,14 +54978,14 @@ var TimeoutError = class extends Error {
|
|
|
53636
54978
|
|
|
53637
54979
|
// node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/timeout.js
|
|
53638
54980
|
async function timeout(request, init, abortController, options) {
|
|
53639
|
-
return new Promise((
|
|
54981
|
+
return new Promise((resolve7, reject) => {
|
|
53640
54982
|
const timeoutId = setTimeout(() => {
|
|
53641
54983
|
if (abortController) {
|
|
53642
54984
|
abortController.abort();
|
|
53643
54985
|
}
|
|
53644
54986
|
reject(new TimeoutError(request));
|
|
53645
54987
|
}, options.timeout);
|
|
53646
|
-
void options.fetch(request, init).then(
|
|
54988
|
+
void options.fetch(request, init).then(resolve7).catch(reject).then(() => {
|
|
53647
54989
|
clearTimeout(timeoutId);
|
|
53648
54990
|
});
|
|
53649
54991
|
});
|
|
@@ -53651,7 +54993,7 @@ async function timeout(request, init, abortController, options) {
|
|
|
53651
54993
|
|
|
53652
54994
|
// node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/delay.js
|
|
53653
54995
|
async function delay(ms, { signal }) {
|
|
53654
|
-
return new Promise((
|
|
54996
|
+
return new Promise((resolve7, reject) => {
|
|
53655
54997
|
if (signal) {
|
|
53656
54998
|
signal.throwIfAborted();
|
|
53657
54999
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
@@ -53662,7 +55004,7 @@ async function delay(ms, { signal }) {
|
|
|
53662
55004
|
}
|
|
53663
55005
|
const timeoutId = setTimeout(() => {
|
|
53664
55006
|
signal?.removeEventListener("abort", abortHandler);
|
|
53665
|
-
|
|
55007
|
+
resolve7();
|
|
53666
55008
|
}, ms);
|
|
53667
55009
|
});
|
|
53668
55010
|
}
|
|
@@ -55291,7 +56633,7 @@ var UpdateNotifier = class {
|
|
|
55291
56633
|
if (Date.now() - this.config.get("lastUpdateCheck") < this.#updateCheckInterval) {
|
|
55292
56634
|
return;
|
|
55293
56635
|
}
|
|
55294
|
-
|
|
56636
|
+
spawn4(process11.execPath, [path9.join(__dirname3, "check.js"), JSON.stringify(this.#options)], {
|
|
55295
56637
|
detached: true,
|
|
55296
56638
|
stdio: "ignore"
|
|
55297
56639
|
}).unref();
|