@osfactory/har 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +641 -208
- package/dist/templates/cursor-rule.mdc.template +8 -7
- package/dist/templates/har-boilerplate/README.md +10 -8
- package/dist/templates/har-boilerplate/agent-slot.sh +11 -0
- package/dist/templates/har-boilerplate-cli/README.md +10 -8
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +11 -0
- package/dist/templates/har-boilerplate-ios/README.md +7 -3
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -765,14 +765,14 @@ var require_url_state_machine = __commonJS({
|
|
|
765
765
|
return url.replace(/\u0009|\u000A|\u000D/g, "");
|
|
766
766
|
}
|
|
767
767
|
function shortenPath(url) {
|
|
768
|
-
const
|
|
769
|
-
if (
|
|
768
|
+
const path51 = url.path;
|
|
769
|
+
if (path51.length === 0) {
|
|
770
770
|
return;
|
|
771
771
|
}
|
|
772
|
-
if (url.scheme === "file" &&
|
|
772
|
+
if (url.scheme === "file" && path51.length === 1 && isNormalizedWindowsDriveLetter(path51[0])) {
|
|
773
773
|
return;
|
|
774
774
|
}
|
|
775
|
-
|
|
775
|
+
path51.pop();
|
|
776
776
|
}
|
|
777
777
|
function includesCredentials(url) {
|
|
778
778
|
return url.username !== "" || url.password !== "";
|
|
@@ -2092,7 +2092,7 @@ var require_lib2 = __commonJS({
|
|
|
2092
2092
|
let accum = [];
|
|
2093
2093
|
let accumBytes = 0;
|
|
2094
2094
|
let abort = false;
|
|
2095
|
-
return new Body.Promise(function(
|
|
2095
|
+
return new Body.Promise(function(resolve36, reject) {
|
|
2096
2096
|
let resTimeout;
|
|
2097
2097
|
if (_this4.timeout) {
|
|
2098
2098
|
resTimeout = setTimeout(function() {
|
|
@@ -2126,7 +2126,7 @@ var require_lib2 = __commonJS({
|
|
|
2126
2126
|
}
|
|
2127
2127
|
clearTimeout(resTimeout);
|
|
2128
2128
|
try {
|
|
2129
|
-
|
|
2129
|
+
resolve36(Buffer.concat(accum, accumBytes));
|
|
2130
2130
|
} catch (err) {
|
|
2131
2131
|
reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
|
|
2132
2132
|
}
|
|
@@ -2801,7 +2801,7 @@ var require_lib2 = __commonJS({
|
|
|
2801
2801
|
throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
|
|
2802
2802
|
}
|
|
2803
2803
|
Body.Promise = fetch3.Promise;
|
|
2804
|
-
return new fetch3.Promise(function(
|
|
2804
|
+
return new fetch3.Promise(function(resolve36, reject) {
|
|
2805
2805
|
const request = new Request3(url, opts);
|
|
2806
2806
|
const options = getNodeRequestOptions(request);
|
|
2807
2807
|
const send = (options.protocol === "https:" ? https : http).request;
|
|
@@ -2934,7 +2934,7 @@ var require_lib2 = __commonJS({
|
|
|
2934
2934
|
requestOpts.body = void 0;
|
|
2935
2935
|
requestOpts.headers.delete("content-length");
|
|
2936
2936
|
}
|
|
2937
|
-
|
|
2937
|
+
resolve36(fetch3(new Request3(locationURL, requestOpts)));
|
|
2938
2938
|
finalize();
|
|
2939
2939
|
return;
|
|
2940
2940
|
}
|
|
@@ -2955,7 +2955,7 @@ var require_lib2 = __commonJS({
|
|
|
2955
2955
|
const codings = headers.get("Content-Encoding");
|
|
2956
2956
|
if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
|
|
2957
2957
|
response = new Response3(body, response_options);
|
|
2958
|
-
|
|
2958
|
+
resolve36(response);
|
|
2959
2959
|
return;
|
|
2960
2960
|
}
|
|
2961
2961
|
const zlibOptions = {
|
|
@@ -2965,7 +2965,7 @@ var require_lib2 = __commonJS({
|
|
|
2965
2965
|
if (codings == "gzip" || codings == "x-gzip") {
|
|
2966
2966
|
body = body.pipe(zlib.createGunzip(zlibOptions));
|
|
2967
2967
|
response = new Response3(body, response_options);
|
|
2968
|
-
|
|
2968
|
+
resolve36(response);
|
|
2969
2969
|
return;
|
|
2970
2970
|
}
|
|
2971
2971
|
if (codings == "deflate" || codings == "x-deflate") {
|
|
@@ -2977,12 +2977,12 @@ var require_lib2 = __commonJS({
|
|
|
2977
2977
|
body = body.pipe(zlib.createInflateRaw());
|
|
2978
2978
|
}
|
|
2979
2979
|
response = new Response3(body, response_options);
|
|
2980
|
-
|
|
2980
|
+
resolve36(response);
|
|
2981
2981
|
});
|
|
2982
2982
|
raw.on("end", function() {
|
|
2983
2983
|
if (!response) {
|
|
2984
2984
|
response = new Response3(body, response_options);
|
|
2985
|
-
|
|
2985
|
+
resolve36(response);
|
|
2986
2986
|
}
|
|
2987
2987
|
});
|
|
2988
2988
|
return;
|
|
@@ -2990,11 +2990,11 @@ var require_lib2 = __commonJS({
|
|
|
2990
2990
|
if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
|
|
2991
2991
|
body = body.pipe(zlib.createBrotliDecompress());
|
|
2992
2992
|
response = new Response3(body, response_options);
|
|
2993
|
-
|
|
2993
|
+
resolve36(response);
|
|
2994
2994
|
return;
|
|
2995
2995
|
}
|
|
2996
2996
|
response = new Response3(body, response_options);
|
|
2997
|
-
|
|
2997
|
+
resolve36(response);
|
|
2998
2998
|
});
|
|
2999
2999
|
writeToStream(req, request);
|
|
3000
3000
|
});
|
|
@@ -6351,14 +6351,14 @@ __export(fileFromPath_exports, {
|
|
|
6351
6351
|
fileFromPathSync: () => fileFromPathSync,
|
|
6352
6352
|
isFile: () => isFile
|
|
6353
6353
|
});
|
|
6354
|
-
function createFileFromPath(
|
|
6354
|
+
function createFileFromPath(path51, { mtimeMs, size }, filenameOrOptions, options = {}) {
|
|
6355
6355
|
let filename;
|
|
6356
6356
|
if (isPlainObject_default2(filenameOrOptions)) {
|
|
6357
6357
|
[options, filename] = [filenameOrOptions, void 0];
|
|
6358
6358
|
} else {
|
|
6359
6359
|
filename = filenameOrOptions;
|
|
6360
6360
|
}
|
|
6361
|
-
const file = new FileFromPath({ path:
|
|
6361
|
+
const file = new FileFromPath({ path: path51, size, lastModified: mtimeMs });
|
|
6362
6362
|
if (!filename) {
|
|
6363
6363
|
filename = file.name;
|
|
6364
6364
|
}
|
|
@@ -6367,13 +6367,13 @@ function createFileFromPath(path50, { mtimeMs, size }, filenameOrOptions, option
|
|
|
6367
6367
|
lastModified: file.lastModified
|
|
6368
6368
|
});
|
|
6369
6369
|
}
|
|
6370
|
-
function fileFromPathSync(
|
|
6371
|
-
const stats = (0, import_fs5.statSync)(
|
|
6372
|
-
return createFileFromPath(
|
|
6370
|
+
function fileFromPathSync(path51, filenameOrOptions, options = {}) {
|
|
6371
|
+
const stats = (0, import_fs5.statSync)(path51);
|
|
6372
|
+
return createFileFromPath(path51, stats, filenameOrOptions, options);
|
|
6373
6373
|
}
|
|
6374
|
-
async function fileFromPath2(
|
|
6375
|
-
const stats = await import_fs5.promises.stat(
|
|
6376
|
-
return createFileFromPath(
|
|
6374
|
+
async function fileFromPath2(path51, filenameOrOptions, options) {
|
|
6375
|
+
const stats = await import_fs5.promises.stat(path51);
|
|
6376
|
+
return createFileFromPath(path51, stats, filenameOrOptions, options);
|
|
6377
6377
|
}
|
|
6378
6378
|
var import_fs5, import_path5, import_node_domexception, __classPrivateFieldSet5, __classPrivateFieldGet6, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
|
|
6379
6379
|
var init_fileFromPath = __esm({
|
|
@@ -9790,7 +9790,7 @@ var require_compile = __commonJS({
|
|
|
9790
9790
|
const schOrFunc = root.refs[ref];
|
|
9791
9791
|
if (schOrFunc)
|
|
9792
9792
|
return schOrFunc;
|
|
9793
|
-
let _sch =
|
|
9793
|
+
let _sch = resolve36.call(this, root, ref);
|
|
9794
9794
|
if (_sch === void 0) {
|
|
9795
9795
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
9796
9796
|
const { schemaId } = this.opts;
|
|
@@ -9817,7 +9817,7 @@ var require_compile = __commonJS({
|
|
|
9817
9817
|
function sameSchemaEnv(s1, s2) {
|
|
9818
9818
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
9819
9819
|
}
|
|
9820
|
-
function
|
|
9820
|
+
function resolve36(root, ref) {
|
|
9821
9821
|
let sch;
|
|
9822
9822
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
9823
9823
|
ref = sch;
|
|
@@ -10035,8 +10035,8 @@ var require_utils2 = __commonJS({
|
|
|
10035
10035
|
}
|
|
10036
10036
|
return ind;
|
|
10037
10037
|
}
|
|
10038
|
-
function removeDotSegments(
|
|
10039
|
-
let input =
|
|
10038
|
+
function removeDotSegments(path51) {
|
|
10039
|
+
let input = path51;
|
|
10040
10040
|
const output = [];
|
|
10041
10041
|
let nextSlash = -1;
|
|
10042
10042
|
let len = 0;
|
|
@@ -10288,8 +10288,8 @@ var require_schemes = __commonJS({
|
|
|
10288
10288
|
wsComponent.secure = void 0;
|
|
10289
10289
|
}
|
|
10290
10290
|
if (wsComponent.resourceName) {
|
|
10291
|
-
const [
|
|
10292
|
-
wsComponent.path =
|
|
10291
|
+
const [path51, query] = wsComponent.resourceName.split("?");
|
|
10292
|
+
wsComponent.path = path51 && path51 !== "/" ? path51 : void 0;
|
|
10293
10293
|
wsComponent.query = query;
|
|
10294
10294
|
wsComponent.resourceName = void 0;
|
|
10295
10295
|
}
|
|
@@ -10448,7 +10448,7 @@ var require_fast_uri = __commonJS({
|
|
|
10448
10448
|
}
|
|
10449
10449
|
return uri;
|
|
10450
10450
|
}
|
|
10451
|
-
function
|
|
10451
|
+
function resolve36(baseURI, relativeURI, options) {
|
|
10452
10452
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
10453
10453
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
10454
10454
|
schemelessOptions.skipEscape = true;
|
|
@@ -10706,7 +10706,7 @@ var require_fast_uri = __commonJS({
|
|
|
10706
10706
|
var fastUri = {
|
|
10707
10707
|
SCHEMES,
|
|
10708
10708
|
normalize: normalize3,
|
|
10709
|
-
resolve:
|
|
10709
|
+
resolve: resolve36,
|
|
10710
10710
|
resolveComponent,
|
|
10711
10711
|
equal,
|
|
10712
10712
|
serialize,
|
|
@@ -13682,12 +13682,12 @@ var require_dist = __commonJS({
|
|
|
13682
13682
|
throw new Error(`Unknown format "${name}"`);
|
|
13683
13683
|
return f2;
|
|
13684
13684
|
};
|
|
13685
|
-
function addFormats(ajv, list,
|
|
13685
|
+
function addFormats(ajv, list, fs43, exportName) {
|
|
13686
13686
|
var _a3;
|
|
13687
13687
|
var _b2;
|
|
13688
13688
|
(_a3 = (_b2 = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
13689
13689
|
for (const f2 of list)
|
|
13690
|
-
ajv.addFormat(f2,
|
|
13690
|
+
ajv.addFormat(f2, fs43[f2]);
|
|
13691
13691
|
}
|
|
13692
13692
|
module2.exports = exports2 = formatsPlugin;
|
|
13693
13693
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -14971,11 +14971,11 @@ var parser = new YargsParser({
|
|
|
14971
14971
|
resolve: import_path2.resolve,
|
|
14972
14972
|
// TODO: figure out a way to combine ESM and CJS coverage, such that
|
|
14973
14973
|
// we can exercise all the lines below:
|
|
14974
|
-
require: (
|
|
14974
|
+
require: (path51) => {
|
|
14975
14975
|
if (typeof require !== "undefined") {
|
|
14976
|
-
return require(
|
|
14977
|
-
} else if (
|
|
14978
|
-
return JSON.parse((0, import_fs2.readFileSync)(
|
|
14976
|
+
return require(path51);
|
|
14977
|
+
} else if (path51.match(/\.json$/)) {
|
|
14978
|
+
return JSON.parse((0, import_fs2.readFileSync)(path51, "utf8"));
|
|
14979
14979
|
} else {
|
|
14980
14980
|
throw Error("only .json config files are supported in ESM");
|
|
14981
14981
|
}
|
|
@@ -17501,12 +17501,12 @@ var YargsInstance = class {
|
|
|
17501
17501
|
async getCompletion(args, done) {
|
|
17502
17502
|
argsert("<array> [function]", [args, done], arguments.length);
|
|
17503
17503
|
if (!done) {
|
|
17504
|
-
return new Promise((
|
|
17504
|
+
return new Promise((resolve36, reject) => {
|
|
17505
17505
|
__classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
|
|
17506
17506
|
if (err)
|
|
17507
17507
|
reject(err);
|
|
17508
17508
|
else
|
|
17509
|
-
|
|
17509
|
+
resolve36(completions);
|
|
17510
17510
|
});
|
|
17511
17511
|
});
|
|
17512
17512
|
} else {
|
|
@@ -19656,8 +19656,8 @@ function getErrorMap() {
|
|
|
19656
19656
|
|
|
19657
19657
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
19658
19658
|
var makeIssue = (params) => {
|
|
19659
|
-
const { data, path:
|
|
19660
|
-
const fullPath = [...
|
|
19659
|
+
const { data, path: path51, errorMaps, issueData } = params;
|
|
19660
|
+
const fullPath = [...path51, ...issueData.path || []];
|
|
19661
19661
|
const fullIssue = {
|
|
19662
19662
|
...issueData,
|
|
19663
19663
|
path: fullPath
|
|
@@ -19773,11 +19773,11 @@ var errorUtil;
|
|
|
19773
19773
|
|
|
19774
19774
|
// node_modules/zod/v3/types.js
|
|
19775
19775
|
var ParseInputLazyPath = class {
|
|
19776
|
-
constructor(parent, value,
|
|
19776
|
+
constructor(parent, value, path51, key) {
|
|
19777
19777
|
this._cachedPath = [];
|
|
19778
19778
|
this.parent = parent;
|
|
19779
19779
|
this.data = value;
|
|
19780
|
-
this._path =
|
|
19780
|
+
this._path = path51;
|
|
19781
19781
|
this._key = key;
|
|
19782
19782
|
}
|
|
19783
19783
|
get path() {
|
|
@@ -23477,7 +23477,26 @@ var RegisterRepoInputSchema = external_exports.object({
|
|
|
23477
23477
|
path: external_exports.string().min(1),
|
|
23478
23478
|
gitRemote: external_exports.string().optional(),
|
|
23479
23479
|
manifest: HarnessManifestSchema.optional(),
|
|
23480
|
-
stagesRegistry: HarnessStageRegistrySchema.optional()
|
|
23480
|
+
stagesRegistry: HarnessStageRegistrySchema.optional(),
|
|
23481
|
+
/** Clear a prior unregister blocklist entry and re-register. */
|
|
23482
|
+
force: external_exports.boolean().optional()
|
|
23483
|
+
});
|
|
23484
|
+
var UnregisterRepoInputSchema = external_exports.object({
|
|
23485
|
+
/** When true, attempt to remove session worktrees on disk (host paths). */
|
|
23486
|
+
deleteWorktrees: external_exports.boolean().optional().default(false)
|
|
23487
|
+
});
|
|
23488
|
+
var UnregisterWorktreeResultSchema = external_exports.object({
|
|
23489
|
+
path: external_exports.string(),
|
|
23490
|
+
agentId: external_exports.number().optional(),
|
|
23491
|
+
deleted: external_exports.boolean(),
|
|
23492
|
+
error: external_exports.string().optional()
|
|
23493
|
+
});
|
|
23494
|
+
var UnregisterRepoResultSchema = external_exports.object({
|
|
23495
|
+
ok: external_exports.literal(true),
|
|
23496
|
+
id: external_exports.string(),
|
|
23497
|
+
path: external_exports.string(),
|
|
23498
|
+
deleteWorktrees: external_exports.boolean(),
|
|
23499
|
+
worktrees: external_exports.array(UnregisterWorktreeResultSchema)
|
|
23481
23500
|
});
|
|
23482
23501
|
var SyncRunsInputSchema = external_exports.object({
|
|
23483
23502
|
runs: external_exports.array(RunRecordSchema)
|
|
@@ -23816,16 +23835,16 @@ async function handleAgentSkills(options) {
|
|
|
23816
23835
|
}
|
|
23817
23836
|
async function askYesNo(question) {
|
|
23818
23837
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
23819
|
-
return new Promise((
|
|
23838
|
+
return new Promise((resolve36) => {
|
|
23820
23839
|
process.stderr.write(`${question} `);
|
|
23821
23840
|
rl.once("line", (answer) => {
|
|
23822
23841
|
rl.close();
|
|
23823
23842
|
const trimmed = answer.trim();
|
|
23824
23843
|
if (trimmed === "") {
|
|
23825
|
-
|
|
23844
|
+
resolve36(true);
|
|
23826
23845
|
return;
|
|
23827
23846
|
}
|
|
23828
|
-
|
|
23847
|
+
resolve36(/^y(es)?$/i.test(trimmed));
|
|
23829
23848
|
});
|
|
23830
23849
|
});
|
|
23831
23850
|
}
|
|
@@ -24683,13 +24702,13 @@ var MultipartBody = class {
|
|
|
24683
24702
|
// node_modules/@anthropic-ai/sdk/_shims/node-runtime.mjs
|
|
24684
24703
|
var import_web = require("node:stream/web");
|
|
24685
24704
|
var fileFromPathWarned = false;
|
|
24686
|
-
async function fileFromPath3(
|
|
24705
|
+
async function fileFromPath3(path51, ...args) {
|
|
24687
24706
|
const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
|
|
24688
24707
|
if (!fileFromPathWarned) {
|
|
24689
|
-
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(
|
|
24708
|
+
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path51)}) instead`);
|
|
24690
24709
|
fileFromPathWarned = true;
|
|
24691
24710
|
}
|
|
24692
|
-
return await _fileFromPath(
|
|
24711
|
+
return await _fileFromPath(path51, ...args);
|
|
24693
24712
|
}
|
|
24694
24713
|
var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
|
|
24695
24714
|
var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
|
|
@@ -25185,8 +25204,8 @@ async function defaultParseResponse(props) {
|
|
|
25185
25204
|
}
|
|
25186
25205
|
var APIPromise = class _APIPromise extends Promise {
|
|
25187
25206
|
constructor(responsePromise, parseResponse = defaultParseResponse) {
|
|
25188
|
-
super((
|
|
25189
|
-
|
|
25207
|
+
super((resolve36) => {
|
|
25208
|
+
resolve36(null);
|
|
25190
25209
|
});
|
|
25191
25210
|
this.responsePromise = responsePromise;
|
|
25192
25211
|
this.parseResponse = parseResponse;
|
|
@@ -25286,29 +25305,29 @@ var APIClient = class {
|
|
|
25286
25305
|
defaultIdempotencyKey() {
|
|
25287
25306
|
return `stainless-node-retry-${uuid4()}`;
|
|
25288
25307
|
}
|
|
25289
|
-
get(
|
|
25290
|
-
return this.methodRequest("get",
|
|
25308
|
+
get(path51, opts) {
|
|
25309
|
+
return this.methodRequest("get", path51, opts);
|
|
25291
25310
|
}
|
|
25292
|
-
post(
|
|
25293
|
-
return this.methodRequest("post",
|
|
25311
|
+
post(path51, opts) {
|
|
25312
|
+
return this.methodRequest("post", path51, opts);
|
|
25294
25313
|
}
|
|
25295
|
-
patch(
|
|
25296
|
-
return this.methodRequest("patch",
|
|
25314
|
+
patch(path51, opts) {
|
|
25315
|
+
return this.methodRequest("patch", path51, opts);
|
|
25297
25316
|
}
|
|
25298
|
-
put(
|
|
25299
|
-
return this.methodRequest("put",
|
|
25317
|
+
put(path51, opts) {
|
|
25318
|
+
return this.methodRequest("put", path51, opts);
|
|
25300
25319
|
}
|
|
25301
|
-
delete(
|
|
25302
|
-
return this.methodRequest("delete",
|
|
25320
|
+
delete(path51, opts) {
|
|
25321
|
+
return this.methodRequest("delete", path51, opts);
|
|
25303
25322
|
}
|
|
25304
|
-
methodRequest(method,
|
|
25323
|
+
methodRequest(method, path51, opts) {
|
|
25305
25324
|
return this.request(Promise.resolve(opts).then(async (opts2) => {
|
|
25306
25325
|
const body = opts2 && isBlobLike(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
|
|
25307
|
-
return { method, path:
|
|
25326
|
+
return { method, path: path51, ...opts2, body };
|
|
25308
25327
|
}));
|
|
25309
25328
|
}
|
|
25310
|
-
getAPIList(
|
|
25311
|
-
return this.requestAPIList(Page, { method: "get", path:
|
|
25329
|
+
getAPIList(path51, Page, opts) {
|
|
25330
|
+
return this.requestAPIList(Page, { method: "get", path: path51, ...opts });
|
|
25312
25331
|
}
|
|
25313
25332
|
calculateContentLength(body) {
|
|
25314
25333
|
if (typeof body === "string") {
|
|
@@ -25326,10 +25345,10 @@ var APIClient = class {
|
|
|
25326
25345
|
return null;
|
|
25327
25346
|
}
|
|
25328
25347
|
buildRequest(options) {
|
|
25329
|
-
const { method, path:
|
|
25348
|
+
const { method, path: path51, query, headers = {} } = options;
|
|
25330
25349
|
const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : isMultipartBody(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
|
|
25331
25350
|
const contentLength = this.calculateContentLength(body);
|
|
25332
|
-
const url = this.buildURL(
|
|
25351
|
+
const url = this.buildURL(path51, query);
|
|
25333
25352
|
if ("timeout" in options)
|
|
25334
25353
|
validatePositiveInteger("timeout", options.timeout);
|
|
25335
25354
|
const timeout = options.timeout ?? this.timeout;
|
|
@@ -25438,8 +25457,8 @@ var APIClient = class {
|
|
|
25438
25457
|
const request = this.makeRequest(options, null);
|
|
25439
25458
|
return new PagePromise(this, request, Page);
|
|
25440
25459
|
}
|
|
25441
|
-
buildURL(
|
|
25442
|
-
const url = isAbsoluteURL(
|
|
25460
|
+
buildURL(path51, query) {
|
|
25461
|
+
const url = isAbsoluteURL(path51) ? new URL(path51) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path51.startsWith("/") ? path51.slice(1) : path51));
|
|
25443
25462
|
const defaultQuery = this.defaultQuery();
|
|
25444
25463
|
if (!isEmptyObj(defaultQuery)) {
|
|
25445
25464
|
query = { ...defaultQuery, ...query };
|
|
@@ -25724,7 +25743,7 @@ var startsWithSchemeRegexp = new RegExp("^(?:[a-z]+:)?//", "i");
|
|
|
25724
25743
|
var isAbsoluteURL = (url) => {
|
|
25725
25744
|
return startsWithSchemeRegexp.test(url);
|
|
25726
25745
|
};
|
|
25727
|
-
var sleep = (ms) => new Promise((
|
|
25746
|
+
var sleep = (ms) => new Promise((resolve36) => setTimeout(resolve36, ms));
|
|
25728
25747
|
var validatePositiveInteger = (name, n2) => {
|
|
25729
25748
|
if (typeof n2 !== "number" || !Number.isInteger(n2)) {
|
|
25730
25749
|
throw new AnthropicError(`${name} must be an integer`);
|
|
@@ -26215,12 +26234,12 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26215
26234
|
}
|
|
26216
26235
|
return this._emit("error", new AnthropicError(String(error3)));
|
|
26217
26236
|
});
|
|
26218
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((
|
|
26219
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise,
|
|
26237
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve36, reject) => {
|
|
26238
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve36, "f");
|
|
26220
26239
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectConnectedPromise, reject, "f");
|
|
26221
26240
|
}), "f");
|
|
26222
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((
|
|
26223
|
-
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise,
|
|
26241
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve36, reject) => {
|
|
26242
|
+
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve36, "f");
|
|
26224
26243
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectEndPromise, reject, "f");
|
|
26225
26244
|
}), "f");
|
|
26226
26245
|
__classPrivateFieldGet8(this, _PromptCachingBetaMessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -26349,11 +26368,11 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26349
26368
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
26350
26369
|
*/
|
|
26351
26370
|
emitted(event) {
|
|
26352
|
-
return new Promise((
|
|
26371
|
+
return new Promise((resolve36, reject) => {
|
|
26353
26372
|
__classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_catchingPromiseCreated, true, "f");
|
|
26354
26373
|
if (event !== "error")
|
|
26355
26374
|
this.once("error", reject);
|
|
26356
|
-
this.once(event,
|
|
26375
|
+
this.once(event, resolve36);
|
|
26357
26376
|
});
|
|
26358
26377
|
}
|
|
26359
26378
|
async done() {
|
|
@@ -26580,7 +26599,7 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
|
|
|
26580
26599
|
if (done) {
|
|
26581
26600
|
return { value: void 0, done: true };
|
|
26582
26601
|
}
|
|
26583
|
-
return new Promise((
|
|
26602
|
+
return new Promise((resolve36, reject) => readQueue.push({ resolve: resolve36, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
26584
26603
|
}
|
|
26585
26604
|
const chunk = pushQueue.shift();
|
|
26586
26605
|
return { value: chunk, done: false };
|
|
@@ -26728,12 +26747,12 @@ var MessageStream = class _MessageStream {
|
|
|
26728
26747
|
}
|
|
26729
26748
|
return this._emit("error", new AnthropicError(String(error3)));
|
|
26730
26749
|
});
|
|
26731
|
-
__classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((
|
|
26732
|
-
__classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise,
|
|
26750
|
+
__classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve36, reject) => {
|
|
26751
|
+
__classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve36, "f");
|
|
26733
26752
|
__classPrivateFieldSet8(this, _MessageStream_rejectConnectedPromise, reject, "f");
|
|
26734
26753
|
}), "f");
|
|
26735
|
-
__classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((
|
|
26736
|
-
__classPrivateFieldSet8(this, _MessageStream_resolveEndPromise,
|
|
26754
|
+
__classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve36, reject) => {
|
|
26755
|
+
__classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve36, "f");
|
|
26737
26756
|
__classPrivateFieldSet8(this, _MessageStream_rejectEndPromise, reject, "f");
|
|
26738
26757
|
}), "f");
|
|
26739
26758
|
__classPrivateFieldGet9(this, _MessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -26862,11 +26881,11 @@ var MessageStream = class _MessageStream {
|
|
|
26862
26881
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
26863
26882
|
*/
|
|
26864
26883
|
emitted(event) {
|
|
26865
|
-
return new Promise((
|
|
26884
|
+
return new Promise((resolve36, reject) => {
|
|
26866
26885
|
__classPrivateFieldSet8(this, _MessageStream_catchingPromiseCreated, true, "f");
|
|
26867
26886
|
if (event !== "error")
|
|
26868
26887
|
this.once("error", reject);
|
|
26869
|
-
this.once(event,
|
|
26888
|
+
this.once(event, resolve36);
|
|
26870
26889
|
});
|
|
26871
26890
|
}
|
|
26872
26891
|
async done() {
|
|
@@ -27093,7 +27112,7 @@ var MessageStream = class _MessageStream {
|
|
|
27093
27112
|
if (done) {
|
|
27094
27113
|
return { value: void 0, done: true };
|
|
27095
27114
|
}
|
|
27096
|
-
return new Promise((
|
|
27115
|
+
return new Promise((resolve36, reject) => readQueue.push({ resolve: resolve36, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
27097
27116
|
}
|
|
27098
27117
|
const chunk = pushQueue.shift();
|
|
27099
27118
|
return { value: chunk, done: false };
|
|
@@ -27353,11 +27372,11 @@ async function promptApplyAgentMdProposal(repoPath) {
|
|
|
27353
27372
|
}
|
|
27354
27373
|
async function askYesNo2(question) {
|
|
27355
27374
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
27356
|
-
return new Promise((
|
|
27375
|
+
return new Promise((resolve36) => {
|
|
27357
27376
|
process.stderr.write(`${question} `);
|
|
27358
27377
|
rl.once("line", (answer) => {
|
|
27359
27378
|
rl.close();
|
|
27360
|
-
|
|
27379
|
+
resolve36(/^y(es)?$/i.test(answer.trim()));
|
|
27361
27380
|
});
|
|
27362
27381
|
});
|
|
27363
27382
|
}
|
|
@@ -27587,7 +27606,7 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
|
|
|
27587
27606
|
}
|
|
27588
27607
|
async function askUser(question, options) {
|
|
27589
27608
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
|
|
27590
|
-
return new Promise((
|
|
27609
|
+
return new Promise((resolve36) => {
|
|
27591
27610
|
process.stderr.write("\n");
|
|
27592
27611
|
process.stderr.write(`\u2753 ${question}
|
|
27593
27612
|
`);
|
|
@@ -27598,7 +27617,7 @@ async function askUser(question, options) {
|
|
|
27598
27617
|
process.stderr.write("> ");
|
|
27599
27618
|
rl.once("line", (answer) => {
|
|
27600
27619
|
rl.close();
|
|
27601
|
-
|
|
27620
|
+
resolve36(answer.trim());
|
|
27602
27621
|
});
|
|
27603
27622
|
});
|
|
27604
27623
|
}
|
|
@@ -27742,7 +27761,7 @@ function runScript(scriptPath, args = [], options = {}) {
|
|
|
27742
27761
|
function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
27743
27762
|
const stream = options.stream ?? false;
|
|
27744
27763
|
const { stream: _stream, ...spawnOptions } = options;
|
|
27745
|
-
return new Promise((
|
|
27764
|
+
return new Promise((resolve36) => {
|
|
27746
27765
|
const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
|
|
27747
27766
|
...spawnOptions,
|
|
27748
27767
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -27757,13 +27776,13 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
|
|
|
27757
27776
|
stderr += d2;
|
|
27758
27777
|
if (stream) process.stderr.write(d2);
|
|
27759
27778
|
});
|
|
27760
|
-
proc.on("close", (code) =>
|
|
27779
|
+
proc.on("close", (code) => resolve36({ stdout, stderr, code: code ?? 0 }));
|
|
27761
27780
|
});
|
|
27762
27781
|
}
|
|
27763
27782
|
function runShellCommand(command2, options = {}) {
|
|
27764
27783
|
const stream = options.stream ?? false;
|
|
27765
27784
|
const { stream: _stream, ...spawnOptions } = options;
|
|
27766
|
-
return new Promise((
|
|
27785
|
+
return new Promise((resolve36) => {
|
|
27767
27786
|
const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
|
|
27768
27787
|
...spawnOptions,
|
|
27769
27788
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -27778,7 +27797,7 @@ function runShellCommand(command2, options = {}) {
|
|
|
27778
27797
|
stderr += d2;
|
|
27779
27798
|
if (stream) process.stderr.write(d2);
|
|
27780
27799
|
});
|
|
27781
|
-
proc.on("close", (code) =>
|
|
27800
|
+
proc.on("close", (code) => resolve36({ stdout, stderr, code: code ?? 0 }));
|
|
27782
27801
|
});
|
|
27783
27802
|
}
|
|
27784
27803
|
|
|
@@ -28942,16 +28961,16 @@ async function promptScaffoldCursorRule(exists) {
|
|
|
28942
28961
|
}
|
|
28943
28962
|
async function askYesNo3(question) {
|
|
28944
28963
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
|
|
28945
|
-
return new Promise((
|
|
28964
|
+
return new Promise((resolve36) => {
|
|
28946
28965
|
process.stderr.write(`${question} `);
|
|
28947
28966
|
rl.once("line", (answer) => {
|
|
28948
28967
|
rl.close();
|
|
28949
28968
|
const trimmed = answer.trim();
|
|
28950
28969
|
if (trimmed === "") {
|
|
28951
|
-
|
|
28970
|
+
resolve36(true);
|
|
28952
28971
|
return;
|
|
28953
28972
|
}
|
|
28954
|
-
|
|
28973
|
+
resolve36(/^y(es)?$/i.test(trimmed));
|
|
28955
28974
|
});
|
|
28956
28975
|
});
|
|
28957
28976
|
}
|
|
@@ -29056,6 +29075,18 @@ function readSlotRegistry(repoPath, agentId) {
|
|
|
29056
29075
|
return void 0;
|
|
29057
29076
|
}
|
|
29058
29077
|
}
|
|
29078
|
+
function listSlotRegistryEntries(repoPath) {
|
|
29079
|
+
const dir = getSlotRegistryDir(repoPath);
|
|
29080
|
+
if (!fs24.existsSync(dir)) return [];
|
|
29081
|
+
const entries = [];
|
|
29082
|
+
for (const name of fs24.readdirSync(dir)) {
|
|
29083
|
+
const match = name.match(/^agent-(\d+)\.json$/);
|
|
29084
|
+
if (!match) continue;
|
|
29085
|
+
const entry = readSlotRegistry(repoPath, Number(match[1]));
|
|
29086
|
+
if (entry) entries.push(entry);
|
|
29087
|
+
}
|
|
29088
|
+
return entries;
|
|
29089
|
+
}
|
|
29059
29090
|
|
|
29060
29091
|
// src/core/local-executor.ts
|
|
29061
29092
|
function quoteShellArg(arg) {
|
|
@@ -29330,6 +29361,20 @@ function recordRepoForControlSync(repoPath) {
|
|
|
29330
29361
|
registry2.repos.push(resolved);
|
|
29331
29362
|
writeRegistry(registry2);
|
|
29332
29363
|
}
|
|
29364
|
+
function removeRegisteredRepo(repoPath) {
|
|
29365
|
+
const resolved = canonicalizeControlRepoPath(repoPath);
|
|
29366
|
+
const registry2 = readRegistry();
|
|
29367
|
+
const next = registry2.repos.filter((entry) => {
|
|
29368
|
+
try {
|
|
29369
|
+
return canonicalizeControlRepoPath(entry) !== resolved;
|
|
29370
|
+
} catch {
|
|
29371
|
+
return path26.resolve(entry) !== resolved;
|
|
29372
|
+
}
|
|
29373
|
+
});
|
|
29374
|
+
if (next.length === registry2.repos.length) return false;
|
|
29375
|
+
writeRegistry({ repos: next });
|
|
29376
|
+
return true;
|
|
29377
|
+
}
|
|
29333
29378
|
function listRegisteredRepos() {
|
|
29334
29379
|
const registry2 = readRegistry();
|
|
29335
29380
|
const kept = [];
|
|
@@ -29777,7 +29822,17 @@ function collectOccupiedSlot(repoPath, agentId) {
|
|
|
29777
29822
|
harnessUsage: "none"
|
|
29778
29823
|
};
|
|
29779
29824
|
}
|
|
29780
|
-
function
|
|
29825
|
+
function describeLaunchBase(repoPath) {
|
|
29826
|
+
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
29827
|
+
const branch = runGit(harnessRoot, "rev-parse --abbrev-ref HEAD") ?? "detached";
|
|
29828
|
+
const sha = runGit(harnessRoot, "rev-parse --short HEAD");
|
|
29829
|
+
return [
|
|
29830
|
+
`New session will be based on: ${branch}${sha ? ` @ ${sha}` : ""}`,
|
|
29831
|
+
` (HEAD of ${harnessRoot})`,
|
|
29832
|
+
" --replace does not choose this base \u2014 switch the main checkout first for a new unrelated task."
|
|
29833
|
+
];
|
|
29834
|
+
}
|
|
29835
|
+
function formatOccupiedSlot(slot, repoPath, resume) {
|
|
29781
29836
|
const lines = [
|
|
29782
29837
|
`Slot ${slot.agentId} is already in use.`,
|
|
29783
29838
|
slot.worktreePath ? ` Worktree: ${slot.worktreePath}` : void 0,
|
|
@@ -29787,6 +29842,8 @@ function formatOccupiedSlot(slot, resume) {
|
|
|
29787
29842
|
slot.lastError ? ` Error: ${slot.lastError}` : void 0,
|
|
29788
29843
|
slot.sessionCreatedAt ? ` Since: ${slot.sessionCreatedAt}` : void 0,
|
|
29789
29844
|
slot.dirty ? " Git: dirty (uncommitted changes \u2014 commit or use force to discard)" : " Git: clean",
|
|
29845
|
+
"",
|
|
29846
|
+
...describeLaunchBase(repoPath),
|
|
29790
29847
|
""
|
|
29791
29848
|
];
|
|
29792
29849
|
if (resume || slot.sessionStatus === "failed" || slot.sessionStatus === "starting") {
|
|
@@ -29799,6 +29856,7 @@ function formatOccupiedSlot(slot, resume) {
|
|
|
29799
29856
|
);
|
|
29800
29857
|
}
|
|
29801
29858
|
lines.push(
|
|
29859
|
+
"Prefer complete/teardown when the previous task is finished, then launch.",
|
|
29802
29860
|
"Replacing removes the worktree. The session branch is kept only if you committed.",
|
|
29803
29861
|
"Gitignored paths (state/, runs/, local clones) are NOT preserved.",
|
|
29804
29862
|
"",
|
|
@@ -29834,7 +29892,7 @@ function checkLaunchGuard(repoPath, agentId, options = {}) {
|
|
|
29834
29892
|
allowed: false,
|
|
29835
29893
|
blocked: true,
|
|
29836
29894
|
slot,
|
|
29837
|
-
reason: formatOccupiedSlot(slot)
|
|
29895
|
+
reason: formatOccupiedSlot(slot, repoPath)
|
|
29838
29896
|
};
|
|
29839
29897
|
}
|
|
29840
29898
|
if (!options.confirmReplace) {
|
|
@@ -29842,7 +29900,7 @@ function checkLaunchGuard(repoPath, agentId, options = {}) {
|
|
|
29842
29900
|
allowed: false,
|
|
29843
29901
|
blocked: true,
|
|
29844
29902
|
slot,
|
|
29845
|
-
reason: formatOccupiedSlot(slot)
|
|
29903
|
+
reason: formatOccupiedSlot(slot, repoPath)
|
|
29846
29904
|
};
|
|
29847
29905
|
}
|
|
29848
29906
|
if (slot.dirty && !options.force) {
|
|
@@ -29851,7 +29909,7 @@ function checkLaunchGuard(repoPath, agentId, options = {}) {
|
|
|
29851
29909
|
blocked: true,
|
|
29852
29910
|
slot,
|
|
29853
29911
|
reason: [
|
|
29854
|
-
formatOccupiedSlot(slot),
|
|
29912
|
+
formatOccupiedSlot(slot, repoPath),
|
|
29855
29913
|
"",
|
|
29856
29914
|
"The occupied worktree has uncommitted changes.",
|
|
29857
29915
|
"Pass force=true / --force to discard them (only after explicit user approval)."
|
|
@@ -30899,7 +30957,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
|
|
|
30899
30957
|
const deadline = Date.now() + timeoutMs;
|
|
30900
30958
|
while (Date.now() < deadline) {
|
|
30901
30959
|
if (await isControlApiReachable(apiUrl)) return true;
|
|
30902
|
-
await new Promise((
|
|
30960
|
+
await new Promise((resolve36) => setTimeout(resolve36, intervalMs));
|
|
30903
30961
|
}
|
|
30904
30962
|
return false;
|
|
30905
30963
|
}
|
|
@@ -30946,9 +31004,24 @@ async function registerRepoWithControl(options) {
|
|
|
30946
31004
|
path: repoPath,
|
|
30947
31005
|
gitRemote: options.gitRemote,
|
|
30948
31006
|
manifest: manifest ?? void 0,
|
|
30949
|
-
stagesRegistry: stagesRegistry ?? void 0
|
|
31007
|
+
stagesRegistry: stagesRegistry ?? void 0,
|
|
31008
|
+
force: options.force
|
|
30950
31009
|
};
|
|
30951
|
-
|
|
31010
|
+
if (options.dryRun) return null;
|
|
31011
|
+
const response = await fetch(`${apiUrl}/api/repos`, {
|
|
31012
|
+
method: "POST",
|
|
31013
|
+
headers: { "Content-Type": "application/json" },
|
|
31014
|
+
body: JSON.stringify(body)
|
|
31015
|
+
});
|
|
31016
|
+
if (response.status === 409) {
|
|
31017
|
+
removeRegisteredRepo(repoPath);
|
|
31018
|
+
return null;
|
|
31019
|
+
}
|
|
31020
|
+
if (!response.ok) {
|
|
31021
|
+
const text = await response.text().catch(() => "");
|
|
31022
|
+
throw new Error(`Control API ${response.status}: ${text || response.statusText}`);
|
|
31023
|
+
}
|
|
31024
|
+
return await response.json();
|
|
30952
31025
|
}
|
|
30953
31026
|
async function syncRepoWithControl(options) {
|
|
30954
31027
|
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
@@ -30980,7 +31053,9 @@ async function syncRepoWithControl(options) {
|
|
|
30980
31053
|
if (!listResponse.ok) throw new Error("Failed to list repos from Control API");
|
|
30981
31054
|
const repos = await listResponse.json();
|
|
30982
31055
|
const existing = repos.find((r2) => path39.resolve(r2.path) === repoPath);
|
|
30983
|
-
if (!existing)
|
|
31056
|
+
if (!existing) {
|
|
31057
|
+
return;
|
|
31058
|
+
}
|
|
30984
31059
|
await syncRepoRunsAndSlots(apiUrl, existing.id, repoPath, options.dryRun);
|
|
30985
31060
|
return;
|
|
30986
31061
|
}
|
|
@@ -31676,6 +31751,9 @@ Usage from Cursor / Claude / Codex (opentelemetry-hooks) will appear under Workt
|
|
|
31676
31751
|
}
|
|
31677
31752
|
};
|
|
31678
31753
|
var defaultRunService = new RunService();
|
|
31754
|
+
function createRunService(executor = localScriptExecutor) {
|
|
31755
|
+
return new RunService(executor);
|
|
31756
|
+
}
|
|
31679
31757
|
var runStage = defaultRunService.runStage.bind(defaultRunService);
|
|
31680
31758
|
var listArtifacts = defaultRunService.listArtifacts.bind(defaultRunService);
|
|
31681
31759
|
var launchEnvironment = defaultRunService.launchEnvironment.bind(defaultRunService);
|
|
@@ -31686,10 +31764,59 @@ var completeEnvironment = defaultRunService.completeEnvironment.bind(defaultRunS
|
|
|
31686
31764
|
var getEnvironmentStatus = defaultRunService.getEnvironmentStatus.bind(defaultRunService);
|
|
31687
31765
|
var getEnvironmentLogs = defaultRunService.getEnvironmentLogs.bind(defaultRunService);
|
|
31688
31766
|
|
|
31767
|
+
// src/cli/help-text.ts
|
|
31768
|
+
var HAR_ROOT_EPILOG = `Typical agent workflow:
|
|
31769
|
+
har env status # see which slots are free / occupied
|
|
31770
|
+
har env launch <id> # new session from the main checkout's HEAD
|
|
31771
|
+
# \u2026edit only under the printed work dir\u2026
|
|
31772
|
+
har env verify <id> --full
|
|
31773
|
+
har env complete <id> # done: verify + free the slot (branch kept for PR)
|
|
31774
|
+
|
|
31775
|
+
Slot lifecycle (do not confuse these):
|
|
31776
|
+
launch start a NEW session (worktree from $REPO_ROOT HEAD)
|
|
31777
|
+
recover resume a failed/partial launch (keeps the existing worktree)
|
|
31778
|
+
complete finish successfully: full verify, then teardown (frees the slot)
|
|
31779
|
+
teardown abandon / free the slot without a completion validation
|
|
31780
|
+
|
|
31781
|
+
--replace only confirms destroying a previous session on that slot id
|
|
31782
|
+
it does NOT choose main \u2014 new session still uses current HEAD
|
|
31783
|
+
--force additionally discard dirty uncommitted work (user approval only)
|
|
31784
|
+
--resume recover failed/starting; never use --replace for that
|
|
31785
|
+
|
|
31786
|
+
For a new unrelated task: checkout main (or your intended base) on the main
|
|
31787
|
+
repo checkout, free the slot (complete/teardown), then launch.`;
|
|
31788
|
+
var HAR_ENV_EPILOG = `Environment lifecycle:
|
|
31789
|
+
preflight <id> dry-run: ports, PM2, Docker, occupied-slot gates
|
|
31790
|
+
launch <id> fresh session worktree from the main checkout's current HEAD
|
|
31791
|
+
recover <id> alias for launch --resume (failed/starting only)
|
|
31792
|
+
verify <id> run checks (--full before declaring done)
|
|
31793
|
+
complete <id> full verify + teardown; keeps branch for push/PR (frees slot)
|
|
31794
|
+
teardown <id> free the slot without recording a completion validation
|
|
31795
|
+
|
|
31796
|
+
Occupied slots:
|
|
31797
|
+
Prefer complete/teardown when the previous task is finished, then launch.
|
|
31798
|
+
Use --replace only to reuse the same slot id immediately (destroys old worktree).
|
|
31799
|
+
--replace does not select the base branch \u2014 HEAD of --repo does.
|
|
31800
|
+
Dirty previous worktree also needs --force after explicit user approval.`;
|
|
31801
|
+
var LAUNCH_COMMAND_DESCRIBE = "Start a fresh agent session (new worktree from the main checkout HEAD)";
|
|
31802
|
+
var LAUNCH_REPLACE_DESCRIBE = "Destroy the previous session on this slot and start another (does not choose main; still uses --repo HEAD)";
|
|
31803
|
+
var LAUNCH_FORCE_DESCRIBE = "With --replace: discard uncommitted changes in the old worktree (only after explicit user approval)";
|
|
31804
|
+
var LAUNCH_RESUME_DESCRIBE = "Continue a failed or starting launch without creating a new worktree (prefer over --replace)";
|
|
31805
|
+
var LAUNCH_EPILOG = `Every launch creates a NEW session from the current HEAD of --repo (the main
|
|
31806
|
+
checkout). The worktree path encodes that base branch \u2014 switch --repo to main
|
|
31807
|
+
before launch for a new unrelated task unless you intentionally stack on a
|
|
31808
|
+
feature branch.
|
|
31809
|
+
|
|
31810
|
+
Free slot: har env launch 1
|
|
31811
|
+
Occupied: har env complete 1 # or teardown \u2014 then launch
|
|
31812
|
+
har env launch 1 --replace # reuse slot id now
|
|
31813
|
+
har env launch 1 --replace --force # + discard dirty work
|
|
31814
|
+
Failed launch: har env recover 1 # or: har env launch 1 --resume`;
|
|
31815
|
+
|
|
31689
31816
|
// src/cli/commands/env.ts
|
|
31690
31817
|
var envCommand = {
|
|
31691
31818
|
command: "env <subcommand>",
|
|
31692
|
-
describe: "Manage agent environments",
|
|
31819
|
+
describe: "Manage agent environments (launch \u2192 verify \u2192 complete)",
|
|
31693
31820
|
builder: (yargs) => yargs.command(
|
|
31694
31821
|
"init",
|
|
31695
31822
|
"Copy harness boilerplate into .har/ (use --auto for built-in Claude adaptation)",
|
|
@@ -31784,24 +31911,28 @@ var envCommand = {
|
|
|
31784
31911
|
handleAddStage
|
|
31785
31912
|
).command(
|
|
31786
31913
|
"launch <id>",
|
|
31787
|
-
|
|
31788
|
-
(y2) => y2.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", {
|
|
31914
|
+
LAUNCH_COMMAND_DESCRIBE,
|
|
31915
|
+
(y2) => y2.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", {
|
|
31916
|
+
type: "string",
|
|
31917
|
+
default: ".",
|
|
31918
|
+
describe: "Main checkout whose HEAD becomes the new session base"
|
|
31919
|
+
}).option("worktree", {
|
|
31789
31920
|
type: "boolean",
|
|
31790
31921
|
default: true,
|
|
31791
31922
|
describe: "Use an isolated git worktree (default)"
|
|
31792
31923
|
}).option("claude", { type: "boolean", default: false }).option("replace", {
|
|
31793
31924
|
type: "boolean",
|
|
31794
31925
|
default: false,
|
|
31795
|
-
describe:
|
|
31926
|
+
describe: LAUNCH_REPLACE_DESCRIBE
|
|
31796
31927
|
}).option("force", {
|
|
31797
31928
|
type: "boolean",
|
|
31798
31929
|
default: false,
|
|
31799
|
-
describe:
|
|
31930
|
+
describe: LAUNCH_FORCE_DESCRIBE
|
|
31800
31931
|
}).option("resume", {
|
|
31801
31932
|
type: "boolean",
|
|
31802
31933
|
default: false,
|
|
31803
|
-
describe:
|
|
31804
|
-
}),
|
|
31934
|
+
describe: LAUNCH_RESUME_DESCRIBE
|
|
31935
|
+
}).epilog(LAUNCH_EPILOG),
|
|
31805
31936
|
handleLaunch
|
|
31806
31937
|
).command(
|
|
31807
31938
|
"recover <id>",
|
|
@@ -31869,7 +32000,10 @@ var envCommand = {
|
|
|
31869
32000
|
).demandCommand(1, "Use: runs list | runs get <runId>"),
|
|
31870
32001
|
() => {
|
|
31871
32002
|
}
|
|
31872
|
-
).demandCommand(
|
|
32003
|
+
).demandCommand(
|
|
32004
|
+
1,
|
|
32005
|
+
"Please specify a subcommand: init, maintain, add-stage, launch, recover, verify, complete, teardown, status, runs"
|
|
32006
|
+
).epilog(HAR_ENV_EPILOG),
|
|
31873
32007
|
handler: () => {
|
|
31874
32008
|
}
|
|
31875
32009
|
};
|
|
@@ -32146,8 +32280,8 @@ async function handleLaunch(argv) {
|
|
|
32146
32280
|
console.error(guard.reason ?? "");
|
|
32147
32281
|
const readline5 = await import("readline");
|
|
32148
32282
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
32149
|
-
const answer = await new Promise((
|
|
32150
|
-
rl.question("Replace this slot? [y/N] ",
|
|
32283
|
+
const answer = await new Promise((resolve36) => {
|
|
32284
|
+
rl.question("Replace this slot? [y/N] ", resolve36);
|
|
32151
32285
|
});
|
|
32152
32286
|
rl.close();
|
|
32153
32287
|
if (!/^[Yy]$/.test(answer.trim())) {
|
|
@@ -32163,8 +32297,8 @@ async function handleLaunch(argv) {
|
|
|
32163
32297
|
warn("Worktree has uncommitted changes.");
|
|
32164
32298
|
const readline5 = await import("readline");
|
|
32165
32299
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
32166
|
-
const answer = await new Promise((
|
|
32167
|
-
rl.question("Discard uncommitted changes? [y/N] ",
|
|
32300
|
+
const answer = await new Promise((resolve36) => {
|
|
32301
|
+
rl.question("Discard uncommitted changes? [y/N] ", resolve36);
|
|
32168
32302
|
});
|
|
32169
32303
|
rl.close();
|
|
32170
32304
|
if (!/^[Yy]$/.test(answer.trim())) {
|
|
@@ -32371,8 +32505,213 @@ function printNextSteps(auto2) {
|
|
|
32371
32505
|
}
|
|
32372
32506
|
|
|
32373
32507
|
// src/cli/commands/control.ts
|
|
32508
|
+
var path45 = __toESM(require("path"));
|
|
32509
|
+
init_control_config();
|
|
32510
|
+
|
|
32511
|
+
// src/core/control-unregister.ts
|
|
32512
|
+
var fs40 = __toESM(require("fs"));
|
|
32374
32513
|
var path44 = __toESM(require("path"));
|
|
32375
32514
|
init_control_config();
|
|
32515
|
+
function listUnregisterWorktreeCandidates(repoPath) {
|
|
32516
|
+
const harnessRoot = canonicalizeControlRepoPath(repoPath);
|
|
32517
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
32518
|
+
const add = (partial2) => {
|
|
32519
|
+
const resolved = path44.resolve(partial2.worktreePath);
|
|
32520
|
+
if (!resolved || resolved === harnessRoot) return;
|
|
32521
|
+
const exists = partial2.exists ?? fs40.existsSync(resolved);
|
|
32522
|
+
const prev = byPath.get(resolved);
|
|
32523
|
+
if (prev) {
|
|
32524
|
+
byPath.set(resolved, {
|
|
32525
|
+
...prev,
|
|
32526
|
+
dirty: prev.dirty || partial2.dirty,
|
|
32527
|
+
branch: prev.branch ?? partial2.branch,
|
|
32528
|
+
workDir: prev.workDir ?? partial2.workDir
|
|
32529
|
+
});
|
|
32530
|
+
return;
|
|
32531
|
+
}
|
|
32532
|
+
byPath.set(resolved, {
|
|
32533
|
+
agentId: partial2.agentId,
|
|
32534
|
+
worktreePath: resolved,
|
|
32535
|
+
workDir: partial2.workDir,
|
|
32536
|
+
branch: partial2.branch,
|
|
32537
|
+
dirty: partial2.dirty,
|
|
32538
|
+
exists
|
|
32539
|
+
});
|
|
32540
|
+
};
|
|
32541
|
+
for (const entry of listSlotRegistryEntries(harnessRoot)) {
|
|
32542
|
+
const worktreePath = entry.worktreePath ?? entry.workDir;
|
|
32543
|
+
if (!worktreePath) continue;
|
|
32544
|
+
add({
|
|
32545
|
+
agentId: entry.agentId,
|
|
32546
|
+
worktreePath,
|
|
32547
|
+
workDir: entry.workDir,
|
|
32548
|
+
branch: entry.branch
|
|
32549
|
+
});
|
|
32550
|
+
}
|
|
32551
|
+
try {
|
|
32552
|
+
const status = collectEnvironmentStatus(harnessRoot);
|
|
32553
|
+
for (const slot of status.slots) {
|
|
32554
|
+
const worktreePath = slot.worktreePath ?? slot.workDir;
|
|
32555
|
+
if (!worktreePath) continue;
|
|
32556
|
+
add({
|
|
32557
|
+
agentId: slot.agentId,
|
|
32558
|
+
worktreePath,
|
|
32559
|
+
workDir: slot.workDir,
|
|
32560
|
+
branch: slot.branch,
|
|
32561
|
+
dirty: slot.dirty === true
|
|
32562
|
+
});
|
|
32563
|
+
}
|
|
32564
|
+
} catch {
|
|
32565
|
+
}
|
|
32566
|
+
return [...byPath.values()].sort((a2, b2) => a2.agentId - b2.agentId);
|
|
32567
|
+
}
|
|
32568
|
+
async function askYesNo4(question) {
|
|
32569
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
32570
|
+
const readline5 = await import("readline");
|
|
32571
|
+
const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
|
|
32572
|
+
const answer = await new Promise((resolve36) => {
|
|
32573
|
+
rl.question(question, resolve36);
|
|
32574
|
+
});
|
|
32575
|
+
rl.close();
|
|
32576
|
+
return /^[Yy]$/.test(answer.trim());
|
|
32577
|
+
}
|
|
32578
|
+
async function confirmUnregister(options) {
|
|
32579
|
+
if (options.yes) {
|
|
32580
|
+
return { proceed: true, deleteWorktrees: false };
|
|
32581
|
+
}
|
|
32582
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
32583
|
+
throw new Error("Non-interactive terminal \u2014 pass --yes (and optionally --delete-worktrees)");
|
|
32584
|
+
}
|
|
32585
|
+
const proceed = await askYesNo4("Unregister this repository from Mission Control? [y/N] ");
|
|
32586
|
+
if (!proceed) return { proceed: false, deleteWorktrees: false };
|
|
32587
|
+
const existing = options.worktrees.filter((w2) => w2.exists);
|
|
32588
|
+
if (existing.length === 0) {
|
|
32589
|
+
return { proceed: true, deleteWorktrees: false };
|
|
32590
|
+
}
|
|
32591
|
+
process.stderr.write("\nSession worktrees:\n");
|
|
32592
|
+
for (const wt2 of existing) {
|
|
32593
|
+
const dirty = wt2.dirty ? " (dirty)" : "";
|
|
32594
|
+
process.stderr.write(` agent-${wt2.agentId}: ${wt2.worktreePath}${dirty}
|
|
32595
|
+
`);
|
|
32596
|
+
}
|
|
32597
|
+
const deleteWorktrees = await askYesNo4(
|
|
32598
|
+
`Also delete ${existing.length} session worktree(s) on disk? [y/N] `
|
|
32599
|
+
);
|
|
32600
|
+
return { proceed: true, deleteWorktrees };
|
|
32601
|
+
}
|
|
32602
|
+
async function deleteJson(url, body, dryRun) {
|
|
32603
|
+
if (dryRun) return null;
|
|
32604
|
+
const response = await fetch(url, {
|
|
32605
|
+
method: "DELETE",
|
|
32606
|
+
headers: { "Content-Type": "application/json" },
|
|
32607
|
+
body: JSON.stringify(body)
|
|
32608
|
+
});
|
|
32609
|
+
if (!response.ok) {
|
|
32610
|
+
const text = await response.text().catch(() => "");
|
|
32611
|
+
return { ok: false, status: response.status, text: text || response.statusText };
|
|
32612
|
+
}
|
|
32613
|
+
return { ok: true, data: await response.json() };
|
|
32614
|
+
}
|
|
32615
|
+
async function teardownWorktrees(repoPath, candidates, dryRun) {
|
|
32616
|
+
const outcomes = [];
|
|
32617
|
+
const service = createRunService();
|
|
32618
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32619
|
+
for (const candidate of candidates) {
|
|
32620
|
+
if (!candidate.exists) continue;
|
|
32621
|
+
if (seen.has(candidate.worktreePath)) continue;
|
|
32622
|
+
seen.add(candidate.worktreePath);
|
|
32623
|
+
if (dryRun) {
|
|
32624
|
+
outcomes.push({ path: candidate.worktreePath, agentId: candidate.agentId, deleted: false });
|
|
32625
|
+
continue;
|
|
32626
|
+
}
|
|
32627
|
+
try {
|
|
32628
|
+
const result = await service.teardownEnvironment({
|
|
32629
|
+
repoPath,
|
|
32630
|
+
agentId: candidate.agentId,
|
|
32631
|
+
capture: false,
|
|
32632
|
+
trigger: "cli"
|
|
32633
|
+
});
|
|
32634
|
+
if (result.code !== 0) {
|
|
32635
|
+
if (fs40.existsSync(candidate.worktreePath)) {
|
|
32636
|
+
fs40.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
32637
|
+
}
|
|
32638
|
+
}
|
|
32639
|
+
outcomes.push({
|
|
32640
|
+
path: candidate.worktreePath,
|
|
32641
|
+
agentId: candidate.agentId,
|
|
32642
|
+
deleted: !fs40.existsSync(candidate.worktreePath),
|
|
32643
|
+
error: result.code !== 0 && fs40.existsSync(candidate.worktreePath) ? result.stderr : void 0
|
|
32644
|
+
});
|
|
32645
|
+
} catch (err) {
|
|
32646
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32647
|
+
try {
|
|
32648
|
+
if (fs40.existsSync(candidate.worktreePath)) {
|
|
32649
|
+
fs40.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
32650
|
+
}
|
|
32651
|
+
} catch {
|
|
32652
|
+
}
|
|
32653
|
+
outcomes.push({
|
|
32654
|
+
path: candidate.worktreePath,
|
|
32655
|
+
agentId: candidate.agentId,
|
|
32656
|
+
deleted: !fs40.existsSync(candidate.worktreePath),
|
|
32657
|
+
error: message
|
|
32658
|
+
});
|
|
32659
|
+
}
|
|
32660
|
+
}
|
|
32661
|
+
return outcomes;
|
|
32662
|
+
}
|
|
32663
|
+
async function unregisterRepoWithControl(options) {
|
|
32664
|
+
const repoPath = canonicalizeControlRepoPath(options.repoPath);
|
|
32665
|
+
const apiUrl = options.apiUrl ?? getControlApiUrl();
|
|
32666
|
+
const deleteWorktrees = options.deleteWorktrees === true;
|
|
32667
|
+
const candidates = listUnregisterWorktreeCandidates(repoPath);
|
|
32668
|
+
let id = null;
|
|
32669
|
+
let apiUnreachable = false;
|
|
32670
|
+
let apiWorktrees = [];
|
|
32671
|
+
if (!options.dryRun) {
|
|
32672
|
+
try {
|
|
32673
|
+
const listResponse = await fetch(`${apiUrl}/api/repos`, {
|
|
32674
|
+
signal: AbortSignal.timeout(5e3)
|
|
32675
|
+
});
|
|
32676
|
+
if (listResponse.ok) {
|
|
32677
|
+
const repos = await listResponse.json();
|
|
32678
|
+
id = repos.find((r2) => canonicalizeControlRepoPath(r2.path) === repoPath)?.id ?? null;
|
|
32679
|
+
} else {
|
|
32680
|
+
apiUnreachable = true;
|
|
32681
|
+
}
|
|
32682
|
+
} catch {
|
|
32683
|
+
apiUnreachable = true;
|
|
32684
|
+
}
|
|
32685
|
+
}
|
|
32686
|
+
if (id && !options.dryRun) {
|
|
32687
|
+
const deleted = await deleteJson(`${apiUrl}/api/repos/${id}`, { deleteWorktrees: false }, options.dryRun);
|
|
32688
|
+
if (deleted && !deleted.ok && deleted.status !== 404) {
|
|
32689
|
+
throw new Error(`Control API ${deleted.status}: ${deleted.text}`);
|
|
32690
|
+
}
|
|
32691
|
+
if (deleted?.ok) {
|
|
32692
|
+
apiWorktrees = deleted.data.worktrees ?? [];
|
|
32693
|
+
}
|
|
32694
|
+
} else if (!id && !options.dryRun && !apiUnreachable) {
|
|
32695
|
+
}
|
|
32696
|
+
const removedFromRegistry = options.dryRun ? false : removeRegisteredRepo(repoPath);
|
|
32697
|
+
let worktrees = apiWorktrees;
|
|
32698
|
+
if (deleteWorktrees) {
|
|
32699
|
+
worktrees = await teardownWorktrees(repoPath, candidates, options.dryRun);
|
|
32700
|
+
} else {
|
|
32701
|
+
worktrees = candidates.filter((c2) => c2.exists).map((c2) => ({ path: c2.worktreePath, agentId: c2.agentId, deleted: false }));
|
|
32702
|
+
}
|
|
32703
|
+
return {
|
|
32704
|
+
ok: true,
|
|
32705
|
+
id,
|
|
32706
|
+
path: repoPath,
|
|
32707
|
+
removedFromRegistry,
|
|
32708
|
+
deleteWorktrees,
|
|
32709
|
+
worktrees,
|
|
32710
|
+
apiUnreachable: apiUnreachable || void 0
|
|
32711
|
+
};
|
|
32712
|
+
}
|
|
32713
|
+
|
|
32714
|
+
// src/cli/commands/control.ts
|
|
32376
32715
|
var controlCommand = {
|
|
32377
32716
|
command: "control <subcommand>",
|
|
32378
32717
|
describe: "Mission Control dashboard (local)",
|
|
@@ -32394,8 +32733,26 @@ var controlCommand = {
|
|
|
32394
32733
|
).command(
|
|
32395
32734
|
"register",
|
|
32396
32735
|
"Register a repository with Mission Control and sync runs + slots",
|
|
32397
|
-
(y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("api-url", { type: "string", describe: "Control API URL" }).option("dry-run", { type: "boolean", default: false }),
|
|
32736
|
+
(y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("api-url", { type: "string", describe: "Control API URL" }).option("dry-run", { type: "boolean", default: false }).option("force", {
|
|
32737
|
+
type: "boolean",
|
|
32738
|
+
default: false,
|
|
32739
|
+
describe: "Re-register even if previously unregistered"
|
|
32740
|
+
}),
|
|
32398
32741
|
handleRegister
|
|
32742
|
+
).command(
|
|
32743
|
+
"unregister",
|
|
32744
|
+
"Remove a repository from Mission Control (optionally delete session worktrees)",
|
|
32745
|
+
(y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("api-url", { type: "string", describe: "Control API URL" }).option("dry-run", { type: "boolean", default: false }).option("yes", {
|
|
32746
|
+
alias: "y",
|
|
32747
|
+
type: "boolean",
|
|
32748
|
+
default: false,
|
|
32749
|
+
describe: "Skip confirmation prompts"
|
|
32750
|
+
}).option("delete-worktrees", {
|
|
32751
|
+
type: "boolean",
|
|
32752
|
+
default: false,
|
|
32753
|
+
describe: "Delete session worktrees for this repo (prompted interactively when omitted)"
|
|
32754
|
+
}).option("json", { type: "boolean", default: false }),
|
|
32755
|
+
handleUnregister
|
|
32399
32756
|
).command(
|
|
32400
32757
|
"sync",
|
|
32401
32758
|
"Sync runs and slot status to Mission Control",
|
|
@@ -32415,7 +32772,10 @@ var controlCommand = {
|
|
|
32415
32772
|
"Configure HAR Cloud API credentials (hosted sync)",
|
|
32416
32773
|
(y2) => y2.option("api-key", { type: "string", describe: "HAR Cloud API key" }),
|
|
32417
32774
|
handleLogin
|
|
32418
|
-
).demandCommand(
|
|
32775
|
+
).demandCommand(
|
|
32776
|
+
1,
|
|
32777
|
+
"Please specify a subcommand: up, down, register, unregister, sync, watch, login"
|
|
32778
|
+
),
|
|
32419
32779
|
handler: () => {
|
|
32420
32780
|
}
|
|
32421
32781
|
};
|
|
@@ -32475,14 +32835,18 @@ async function handleDown() {
|
|
|
32475
32835
|
process.exit(code);
|
|
32476
32836
|
}
|
|
32477
32837
|
async function handleRegister(argv) {
|
|
32478
|
-
const repoPath =
|
|
32838
|
+
const repoPath = path45.resolve(argv.repo);
|
|
32479
32839
|
header("har control register");
|
|
32480
32840
|
info(`Repository: ${repoPath}`);
|
|
32481
32841
|
try {
|
|
32842
|
+
if (!argv.dryRun) {
|
|
32843
|
+
recordRepoForControlSync(repoPath);
|
|
32844
|
+
}
|
|
32482
32845
|
await syncRepoWithControl({
|
|
32483
32846
|
repoPath,
|
|
32484
32847
|
apiUrl: argv.apiUrl,
|
|
32485
|
-
dryRun: argv.dryRun
|
|
32848
|
+
dryRun: argv.dryRun,
|
|
32849
|
+
force: true
|
|
32486
32850
|
});
|
|
32487
32851
|
if (argv.dryRun) {
|
|
32488
32852
|
info("Dry run \u2014 no API call made");
|
|
@@ -32494,8 +32858,77 @@ async function handleRegister(argv) {
|
|
|
32494
32858
|
process.exit(1);
|
|
32495
32859
|
}
|
|
32496
32860
|
}
|
|
32861
|
+
async function handleUnregister(argv) {
|
|
32862
|
+
const repoPath = path45.resolve(argv.repo);
|
|
32863
|
+
const worktrees = listUnregisterWorktreeCandidates(repoPath);
|
|
32864
|
+
try {
|
|
32865
|
+
let deleteWorktrees = argv.deleteWorktrees;
|
|
32866
|
+
if (!argv.yes && !argv.dryRun) {
|
|
32867
|
+
const confirmation = await confirmUnregister({ yes: false, worktrees });
|
|
32868
|
+
if (!confirmation.proceed) {
|
|
32869
|
+
error("Aborted \u2014 repository left registered.");
|
|
32870
|
+
process.exit(2);
|
|
32871
|
+
}
|
|
32872
|
+
deleteWorktrees = argv.deleteWorktrees || confirmation.deleteWorktrees;
|
|
32873
|
+
} else if (argv.yes && !argv.deleteWorktrees && worktrees.some((w2) => w2.exists)) {
|
|
32874
|
+
deleteWorktrees = false;
|
|
32875
|
+
}
|
|
32876
|
+
const result = await unregisterRepoWithControl({
|
|
32877
|
+
repoPath,
|
|
32878
|
+
apiUrl: argv.apiUrl,
|
|
32879
|
+
dryRun: argv.dryRun,
|
|
32880
|
+
deleteWorktrees
|
|
32881
|
+
});
|
|
32882
|
+
if (argv.json) {
|
|
32883
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
32884
|
+
return;
|
|
32885
|
+
}
|
|
32886
|
+
header("har control unregister");
|
|
32887
|
+
info(`Repository: ${result.path}`);
|
|
32888
|
+
if (argv.dryRun) {
|
|
32889
|
+
info("Dry run \u2014 no changes made");
|
|
32890
|
+
if (worktrees.filter((w2) => w2.exists).length > 0) {
|
|
32891
|
+
info(
|
|
32892
|
+
`Would propose deleting ${worktrees.filter((w2) => w2.exists).length} session worktree(s)`
|
|
32893
|
+
);
|
|
32894
|
+
}
|
|
32895
|
+
return;
|
|
32896
|
+
}
|
|
32897
|
+
if (result.apiUnreachable) {
|
|
32898
|
+
warn("Mission Control API unreachable \u2014 removed from local registry only");
|
|
32899
|
+
} else if (result.id) {
|
|
32900
|
+
success("Removed from Mission Control");
|
|
32901
|
+
} else {
|
|
32902
|
+
info("Repository was not present in Mission Control");
|
|
32903
|
+
}
|
|
32904
|
+
if (result.removedFromRegistry) {
|
|
32905
|
+
success("Removed from local sync registry (~/.har/repos.json)");
|
|
32906
|
+
}
|
|
32907
|
+
if (result.deleteWorktrees) {
|
|
32908
|
+
const deleted = result.worktrees.filter((w2) => w2.deleted).length;
|
|
32909
|
+
const failed = result.worktrees.filter((w2) => !w2.deleted && w2.error);
|
|
32910
|
+
success(`Deleted ${deleted} session worktree(s)`);
|
|
32911
|
+
for (const failure of failed) {
|
|
32912
|
+
warn(`Failed to delete ${failure.path}: ${failure.error}`);
|
|
32913
|
+
}
|
|
32914
|
+
} else if (result.worktrees.length > 0) {
|
|
32915
|
+
info(
|
|
32916
|
+
`Left ${result.worktrees.length} session worktree(s) on disk (rerun with --delete-worktrees to remove)`
|
|
32917
|
+
);
|
|
32918
|
+
}
|
|
32919
|
+
} catch (err) {
|
|
32920
|
+
if (argv.json) {
|
|
32921
|
+
process.stdout.write(
|
|
32922
|
+
JSON.stringify({ ok: false, error: err.message }, null, 2) + "\n"
|
|
32923
|
+
);
|
|
32924
|
+
process.exit(1);
|
|
32925
|
+
}
|
|
32926
|
+
error(err.message);
|
|
32927
|
+
process.exit(1);
|
|
32928
|
+
}
|
|
32929
|
+
}
|
|
32497
32930
|
async function handleSync(argv) {
|
|
32498
|
-
const repoPath =
|
|
32931
|
+
const repoPath = path45.resolve(argv.repo);
|
|
32499
32932
|
try {
|
|
32500
32933
|
await syncRepoWithControl({
|
|
32501
32934
|
repoPath,
|
|
@@ -32534,7 +32967,7 @@ async function handleWatch(argv) {
|
|
|
32534
32967
|
};
|
|
32535
32968
|
const tick = async () => {
|
|
32536
32969
|
if (argv.repo) {
|
|
32537
|
-
await syncOne(
|
|
32970
|
+
await syncOne(path45.resolve(argv.repo));
|
|
32538
32971
|
return;
|
|
32539
32972
|
}
|
|
32540
32973
|
try {
|
|
@@ -32565,12 +32998,12 @@ async function handleLogin(argv) {
|
|
|
32565
32998
|
}
|
|
32566
32999
|
|
|
32567
33000
|
// src/cli/commands/hooks.ts
|
|
32568
|
-
var
|
|
33001
|
+
var path48 = __toESM(require("path"));
|
|
32569
33002
|
|
|
32570
33003
|
// src/core/hooks.ts
|
|
32571
|
-
var
|
|
33004
|
+
var fs41 = __toESM(require("fs"));
|
|
32572
33005
|
var os13 = __toESM(require("os"));
|
|
32573
|
-
var
|
|
33006
|
+
var path46 = __toESM(require("path"));
|
|
32574
33007
|
var MARKER_START = "# >>> har commit gate (managed by `har hooks`) >>>";
|
|
32575
33008
|
var MARKER_END = "# <<< har commit gate <<<";
|
|
32576
33009
|
var PRE_COMMIT_BLOCK = [
|
|
@@ -32595,7 +33028,7 @@ function resolveCheckoutRoot(cwd) {
|
|
|
32595
33028
|
function resolveHooksDir(checkoutDir) {
|
|
32596
33029
|
const hooksPath = tryGit3(checkoutDir, "rev-parse --git-path hooks");
|
|
32597
33030
|
if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
|
|
32598
|
-
return
|
|
33031
|
+
return path46.resolve(checkoutDir, hooksPath);
|
|
32599
33032
|
}
|
|
32600
33033
|
function getConfiguredHooksPath(checkoutDir) {
|
|
32601
33034
|
return tryGit3(checkoutDir, "config core.hooksPath") || void 0;
|
|
@@ -32603,9 +33036,9 @@ function getConfiguredHooksPath(checkoutDir) {
|
|
|
32603
33036
|
function defaultHarInvocation() {
|
|
32604
33037
|
const entry = process.argv[1];
|
|
32605
33038
|
if (entry && entry.endsWith(".js")) {
|
|
32606
|
-
return `"${process.execPath}" "${
|
|
33039
|
+
return `"${process.execPath}" "${path46.resolve(entry)}"`;
|
|
32607
33040
|
}
|
|
32608
|
-
if (entry) return `"${
|
|
33041
|
+
if (entry) return `"${path46.resolve(entry)}"`;
|
|
32609
33042
|
return "har";
|
|
32610
33043
|
}
|
|
32611
33044
|
function buildHookScript(harInvocation, subcommand, failOpenNotice) {
|
|
@@ -32626,40 +33059,40 @@ exit 0
|
|
|
32626
33059
|
`;
|
|
32627
33060
|
}
|
|
32628
33061
|
function upsertMarkedBlock(filePath, block) {
|
|
32629
|
-
if (!
|
|
32630
|
-
|
|
33062
|
+
if (!fs41.existsSync(filePath)) {
|
|
33063
|
+
fs41.writeFileSync(filePath, `#!/bin/sh
|
|
32631
33064
|
${block}
|
|
32632
33065
|
`, { mode: 493 });
|
|
32633
33066
|
return "created";
|
|
32634
33067
|
}
|
|
32635
|
-
const content =
|
|
33068
|
+
const content = fs41.readFileSync(filePath, "utf8");
|
|
32636
33069
|
if (content.includes(MARKER_START)) {
|
|
32637
33070
|
const pattern = new RegExp(
|
|
32638
33071
|
`${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}`
|
|
32639
33072
|
);
|
|
32640
|
-
|
|
32641
|
-
|
|
33073
|
+
fs41.writeFileSync(filePath, content.replace(pattern, block));
|
|
33074
|
+
fs41.chmodSync(filePath, 493);
|
|
32642
33075
|
return "updated";
|
|
32643
33076
|
}
|
|
32644
33077
|
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
32645
|
-
|
|
33078
|
+
fs41.writeFileSync(filePath, `${content}${suffix}
|
|
32646
33079
|
${block}
|
|
32647
33080
|
`);
|
|
32648
|
-
|
|
33081
|
+
fs41.chmodSync(filePath, 493);
|
|
32649
33082
|
return "appended";
|
|
32650
33083
|
}
|
|
32651
33084
|
function removeMarkedBlock(filePath) {
|
|
32652
|
-
if (!
|
|
32653
|
-
const content =
|
|
33085
|
+
if (!fs41.existsSync(filePath)) return false;
|
|
33086
|
+
const content = fs41.readFileSync(filePath, "utf8");
|
|
32654
33087
|
if (!content.includes(MARKER_START)) return false;
|
|
32655
33088
|
const pattern = new RegExp(
|
|
32656
33089
|
`\\n?${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}\\n?`
|
|
32657
33090
|
);
|
|
32658
33091
|
const stripped = content.replace(pattern, "\n");
|
|
32659
33092
|
if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
|
|
32660
|
-
|
|
33093
|
+
fs41.rmSync(filePath);
|
|
32661
33094
|
} else {
|
|
32662
|
-
|
|
33095
|
+
fs41.writeFileSync(filePath, stripped);
|
|
32663
33096
|
}
|
|
32664
33097
|
return true;
|
|
32665
33098
|
}
|
|
@@ -32681,20 +33114,20 @@ Or re-run with --force to write into that directory anyway.`
|
|
|
32681
33114
|
);
|
|
32682
33115
|
}
|
|
32683
33116
|
const hooksDir = resolveHooksDir(checkout);
|
|
32684
|
-
|
|
33117
|
+
fs41.mkdirSync(hooksDir, { recursive: true });
|
|
32685
33118
|
const invocation = options.harInvocation ?? defaultHarInvocation();
|
|
32686
|
-
|
|
32687
|
-
|
|
33119
|
+
fs41.writeFileSync(
|
|
33120
|
+
path46.join(hooksDir, "har-pre-commit"),
|
|
32688
33121
|
buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
|
|
32689
33122
|
{ mode: 493 }
|
|
32690
33123
|
);
|
|
32691
|
-
|
|
32692
|
-
|
|
33124
|
+
fs41.writeFileSync(
|
|
33125
|
+
path46.join(hooksDir, "har-post-commit"),
|
|
32693
33126
|
buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
|
|
32694
33127
|
{ mode: 493 }
|
|
32695
33128
|
);
|
|
32696
|
-
const preCommit = upsertMarkedBlock(
|
|
32697
|
-
const postCommit = upsertMarkedBlock(
|
|
33129
|
+
const preCommit = upsertMarkedBlock(path46.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
|
|
33130
|
+
const postCommit = upsertMarkedBlock(path46.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
|
|
32698
33131
|
ensureValidationsIgnored(checkout);
|
|
32699
33132
|
return { hooksDir, preCommit, postCommit };
|
|
32700
33133
|
}
|
|
@@ -32703,12 +33136,12 @@ function uninstallHooks(repoPath) {
|
|
|
32703
33136
|
if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
|
|
32704
33137
|
const hooksDir = resolveHooksDir(checkout);
|
|
32705
33138
|
let removed = false;
|
|
32706
|
-
removed = removeMarkedBlock(
|
|
32707
|
-
removed = removeMarkedBlock(
|
|
33139
|
+
removed = removeMarkedBlock(path46.join(hooksDir, "pre-commit")) || removed;
|
|
33140
|
+
removed = removeMarkedBlock(path46.join(hooksDir, "post-commit")) || removed;
|
|
32708
33141
|
for (const file of ["har-pre-commit", "har-post-commit"]) {
|
|
32709
|
-
const full =
|
|
32710
|
-
if (
|
|
32711
|
-
|
|
33142
|
+
const full = path46.join(hooksDir, file);
|
|
33143
|
+
if (fs41.existsSync(full)) {
|
|
33144
|
+
fs41.rmSync(full);
|
|
32712
33145
|
removed = true;
|
|
32713
33146
|
}
|
|
32714
33147
|
}
|
|
@@ -32725,9 +33158,9 @@ function getCommitGateConfig(checkoutDir) {
|
|
|
32725
33158
|
function isAgentWorktree(checkoutDir) {
|
|
32726
33159
|
const branch = getCurrentBranch(checkoutDir);
|
|
32727
33160
|
if (branch && /^har-agent-\d+$/.test(branch)) return true;
|
|
32728
|
-
const worktreesRoot =
|
|
32729
|
-
const resolved =
|
|
32730
|
-
return resolved.startsWith(`${worktreesRoot}${
|
|
33161
|
+
const worktreesRoot = path46.join(os13.homedir(), "worktrees");
|
|
33162
|
+
const resolved = path46.resolve(checkoutDir);
|
|
33163
|
+
return resolved.startsWith(`${worktreesRoot}${path46.sep}`) && /-agent-\d+$/.test(path46.basename(resolved));
|
|
32731
33164
|
}
|
|
32732
33165
|
function resolveEffectiveMode(checkoutDir, gate) {
|
|
32733
33166
|
if (!gate.enabled) return "off";
|
|
@@ -32740,15 +33173,15 @@ function getHooksStatus(repoPath) {
|
|
|
32740
33173
|
const hooksDir = resolveHooksDir(checkout);
|
|
32741
33174
|
const gate = getCommitGateConfig(checkout);
|
|
32742
33175
|
const hasBlock = (name) => {
|
|
32743
|
-
const file =
|
|
32744
|
-
return
|
|
33176
|
+
const file = path46.join(hooksDir, name);
|
|
33177
|
+
return fs41.existsSync(file) && fs41.readFileSync(file, "utf8").includes(MARKER_START);
|
|
32745
33178
|
};
|
|
32746
33179
|
return {
|
|
32747
33180
|
checkout,
|
|
32748
33181
|
hooksDir,
|
|
32749
33182
|
configuredHooksPath: getConfiguredHooksPath(checkout),
|
|
32750
|
-
preCommitInstalled: hasBlock("pre-commit") &&
|
|
32751
|
-
postCommitInstalled: hasBlock("post-commit") &&
|
|
33183
|
+
preCommitInstalled: hasBlock("pre-commit") && fs41.existsSync(path46.join(hooksDir, "har-pre-commit")),
|
|
33184
|
+
postCommitInstalled: hasBlock("post-commit") && fs41.existsSync(path46.join(hooksDir, "har-post-commit")),
|
|
32752
33185
|
gate,
|
|
32753
33186
|
effectiveMode: resolveEffectiveMode(checkout, gate)
|
|
32754
33187
|
};
|
|
@@ -32759,7 +33192,7 @@ function checkCommitGate(cwd) {
|
|
|
32759
33192
|
}
|
|
32760
33193
|
const checkout = resolveCheckoutRoot(cwd);
|
|
32761
33194
|
if (!checkout) return { exitCode: 0, messages: [] };
|
|
32762
|
-
if (!
|
|
33195
|
+
if (!fs41.existsSync(path46.join(checkout, ".har", "stages.json"))) {
|
|
32763
33196
|
return { exitCode: 0, messages: [] };
|
|
32764
33197
|
}
|
|
32765
33198
|
const gate = getCommitGateConfig(checkout);
|
|
@@ -32830,8 +33263,8 @@ async function recordCommitAssociation(cwd) {
|
|
|
32830
33263
|
}
|
|
32831
33264
|
|
|
32832
33265
|
// src/core/claude-hooks.ts
|
|
32833
|
-
var
|
|
32834
|
-
var
|
|
33266
|
+
var fs42 = __toESM(require("fs"));
|
|
33267
|
+
var path47 = __toESM(require("path"));
|
|
32835
33268
|
var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
|
|
32836
33269
|
var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
|
|
32837
33270
|
var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
|
|
@@ -32842,8 +33275,8 @@ function isHarGuardEntry(entry) {
|
|
|
32842
33275
|
);
|
|
32843
33276
|
}
|
|
32844
33277
|
function readSettings(settingsPath) {
|
|
32845
|
-
if (!
|
|
32846
|
-
const raw =
|
|
33278
|
+
if (!fs42.existsSync(settingsPath)) return {};
|
|
33279
|
+
const raw = fs42.readFileSync(settingsPath, "utf8").trim();
|
|
32847
33280
|
if (raw === "") return {};
|
|
32848
33281
|
const parsed = JSON.parse(raw);
|
|
32849
33282
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -32852,19 +33285,19 @@ function readSettings(settingsPath) {
|
|
|
32852
33285
|
return parsed;
|
|
32853
33286
|
}
|
|
32854
33287
|
function writeSettings(settingsPath, settings) {
|
|
32855
|
-
|
|
32856
|
-
|
|
33288
|
+
fs42.mkdirSync(path47.dirname(settingsPath), { recursive: true });
|
|
33289
|
+
fs42.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
32857
33290
|
}
|
|
32858
33291
|
function installClaudeGuard(repoPath) {
|
|
32859
33292
|
const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
|
|
32860
33293
|
if (!templatePath) {
|
|
32861
33294
|
throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
|
|
32862
33295
|
}
|
|
32863
|
-
const guardScript =
|
|
32864
|
-
|
|
32865
|
-
|
|
32866
|
-
|
|
32867
|
-
const settingsPath =
|
|
33296
|
+
const guardScript = path47.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
33297
|
+
fs42.mkdirSync(path47.dirname(guardScript), { recursive: true });
|
|
33298
|
+
fs42.copyFileSync(templatePath, guardScript);
|
|
33299
|
+
fs42.chmodSync(guardScript, 493);
|
|
33300
|
+
const settingsPath = path47.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
32868
33301
|
const settings = readSettings(settingsPath);
|
|
32869
33302
|
const hooks = settings.hooks ?? {};
|
|
32870
33303
|
const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
|
|
@@ -32878,15 +33311,15 @@ function installClaudeGuard(repoPath) {
|
|
|
32878
33311
|
}
|
|
32879
33312
|
function uninstallClaudeGuard(repoPath) {
|
|
32880
33313
|
let removed = false;
|
|
32881
|
-
const guardScript =
|
|
32882
|
-
if (
|
|
32883
|
-
|
|
33314
|
+
const guardScript = path47.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
33315
|
+
if (fs42.existsSync(guardScript)) {
|
|
33316
|
+
fs42.rmSync(guardScript);
|
|
32884
33317
|
removed = true;
|
|
32885
|
-
const parent =
|
|
32886
|
-
if (
|
|
33318
|
+
const parent = path47.dirname(guardScript);
|
|
33319
|
+
if (fs42.existsSync(parent) && fs42.readdirSync(parent).length === 0) fs42.rmdirSync(parent);
|
|
32887
33320
|
}
|
|
32888
|
-
const settingsPath =
|
|
32889
|
-
if (
|
|
33321
|
+
const settingsPath = path47.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
33322
|
+
if (fs42.existsSync(settingsPath)) {
|
|
32890
33323
|
const settings = readSettings(settingsPath);
|
|
32891
33324
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
32892
33325
|
if (preToolUse) {
|
|
@@ -32911,8 +33344,8 @@ function uninstallClaudeGuard(repoPath) {
|
|
|
32911
33344
|
return { removed };
|
|
32912
33345
|
}
|
|
32913
33346
|
function claudeGuardInstalled(repoPath) {
|
|
32914
|
-
const settingsPath =
|
|
32915
|
-
if (!
|
|
33347
|
+
const settingsPath = path47.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
33348
|
+
if (!fs42.existsSync(settingsPath)) return false;
|
|
32916
33349
|
try {
|
|
32917
33350
|
const settings = readSettings(settingsPath);
|
|
32918
33351
|
return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
|
|
@@ -32928,7 +33361,7 @@ function repoOption(y2) {
|
|
|
32928
33361
|
function handleInstall2(argv) {
|
|
32929
33362
|
try {
|
|
32930
33363
|
if (argv.claude) {
|
|
32931
|
-
const result2 = installClaudeGuard(
|
|
33364
|
+
const result2 = installClaudeGuard(path48.resolve(argv.repo));
|
|
32932
33365
|
success("Claude Code worktree guard installed.");
|
|
32933
33366
|
info(`Guard script: ${result2.guardScript}`);
|
|
32934
33367
|
info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
|
|
@@ -32936,7 +33369,7 @@ function handleInstall2(argv) {
|
|
|
32936
33369
|
info("Commit both files so the guard travels with the repo.");
|
|
32937
33370
|
return;
|
|
32938
33371
|
}
|
|
32939
|
-
const result = installHooks({ repoPath:
|
|
33372
|
+
const result = installHooks({ repoPath: path48.resolve(argv.repo), force: argv.force });
|
|
32940
33373
|
success(`Commit gate installed in ${result.hooksDir}`);
|
|
32941
33374
|
info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
|
|
32942
33375
|
info("Commits of unverified change batches will be blocked in agent worktrees.");
|
|
@@ -32949,7 +33382,7 @@ function handleInstall2(argv) {
|
|
|
32949
33382
|
function handleUninstall(argv) {
|
|
32950
33383
|
try {
|
|
32951
33384
|
if (argv.claude) {
|
|
32952
|
-
const result2 = uninstallClaudeGuard(
|
|
33385
|
+
const result2 = uninstallClaudeGuard(path48.resolve(argv.repo));
|
|
32953
33386
|
if (result2.removed) {
|
|
32954
33387
|
success("Claude Code worktree guard removed.");
|
|
32955
33388
|
} else {
|
|
@@ -32957,7 +33390,7 @@ function handleUninstall(argv) {
|
|
|
32957
33390
|
}
|
|
32958
33391
|
return;
|
|
32959
33392
|
}
|
|
32960
|
-
const result = uninstallHooks(
|
|
33393
|
+
const result = uninstallHooks(path48.resolve(argv.repo));
|
|
32961
33394
|
if (result.removed) {
|
|
32962
33395
|
success(`Commit gate removed from ${result.hooksDir}`);
|
|
32963
33396
|
} else {
|
|
@@ -32970,7 +33403,7 @@ function handleUninstall(argv) {
|
|
|
32970
33403
|
}
|
|
32971
33404
|
function handleStatus2(argv) {
|
|
32972
33405
|
try {
|
|
32973
|
-
const status = getHooksStatus(
|
|
33406
|
+
const status = getHooksStatus(path48.resolve(argv.repo));
|
|
32974
33407
|
if (argv.json) {
|
|
32975
33408
|
process.stdout.write(`${JSON.stringify(status, null, 2)}
|
|
32976
33409
|
`);
|
|
@@ -32988,7 +33421,7 @@ function handleStatus2(argv) {
|
|
|
32988
33421
|
);
|
|
32989
33422
|
info(`Effective: ${status.effectiveMode} (in this checkout)`);
|
|
32990
33423
|
info(
|
|
32991
|
-
`Claude guard: ${claudeGuardInstalled(
|
|
33424
|
+
`Claude guard: ${claudeGuardInstalled(path48.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
|
|
32992
33425
|
);
|
|
32993
33426
|
} catch (err) {
|
|
32994
33427
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -33249,10 +33682,10 @@ function assignProp(target, prop, value) {
|
|
|
33249
33682
|
configurable: true
|
|
33250
33683
|
});
|
|
33251
33684
|
}
|
|
33252
|
-
function getElementAtPath(obj,
|
|
33253
|
-
if (!
|
|
33685
|
+
function getElementAtPath(obj, path51) {
|
|
33686
|
+
if (!path51)
|
|
33254
33687
|
return obj;
|
|
33255
|
-
return
|
|
33688
|
+
return path51.reduce((acc, key) => acc?.[key], obj);
|
|
33256
33689
|
}
|
|
33257
33690
|
function promiseAllObject(promisesObj) {
|
|
33258
33691
|
const keys = Object.keys(promisesObj);
|
|
@@ -33572,11 +34005,11 @@ function aborted(x2, startIndex = 0) {
|
|
|
33572
34005
|
}
|
|
33573
34006
|
return false;
|
|
33574
34007
|
}
|
|
33575
|
-
function prefixIssues(
|
|
34008
|
+
function prefixIssues(path51, issues) {
|
|
33576
34009
|
return issues.map((iss) => {
|
|
33577
34010
|
var _a3;
|
|
33578
34011
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
33579
|
-
iss.path.unshift(
|
|
34012
|
+
iss.path.unshift(path51);
|
|
33580
34013
|
return iss;
|
|
33581
34014
|
});
|
|
33582
34015
|
}
|
|
@@ -38899,7 +39332,7 @@ var Protocol = class {
|
|
|
38899
39332
|
return;
|
|
38900
39333
|
}
|
|
38901
39334
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
38902
|
-
await new Promise((
|
|
39335
|
+
await new Promise((resolve36) => setTimeout(resolve36, pollInterval));
|
|
38903
39336
|
options?.signal?.throwIfAborted();
|
|
38904
39337
|
}
|
|
38905
39338
|
} catch (error3) {
|
|
@@ -38916,7 +39349,7 @@ var Protocol = class {
|
|
|
38916
39349
|
*/
|
|
38917
39350
|
request(request, resultSchema, options) {
|
|
38918
39351
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
38919
|
-
return new Promise((
|
|
39352
|
+
return new Promise((resolve36, reject) => {
|
|
38920
39353
|
const earlyReject = (error3) => {
|
|
38921
39354
|
reject(error3);
|
|
38922
39355
|
};
|
|
@@ -38994,7 +39427,7 @@ var Protocol = class {
|
|
|
38994
39427
|
if (!parseResult.success) {
|
|
38995
39428
|
reject(parseResult.error);
|
|
38996
39429
|
} else {
|
|
38997
|
-
|
|
39430
|
+
resolve36(parseResult.data);
|
|
38998
39431
|
}
|
|
38999
39432
|
} catch (error3) {
|
|
39000
39433
|
reject(error3);
|
|
@@ -39255,12 +39688,12 @@ var Protocol = class {
|
|
|
39255
39688
|
}
|
|
39256
39689
|
} catch {
|
|
39257
39690
|
}
|
|
39258
|
-
return new Promise((
|
|
39691
|
+
return new Promise((resolve36, reject) => {
|
|
39259
39692
|
if (signal.aborted) {
|
|
39260
39693
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
39261
39694
|
return;
|
|
39262
39695
|
}
|
|
39263
|
-
const timeoutId = setTimeout(
|
|
39696
|
+
const timeoutId = setTimeout(resolve36, interval);
|
|
39264
39697
|
signal.addEventListener("abort", () => {
|
|
39265
39698
|
clearTimeout(timeoutId);
|
|
39266
39699
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -40050,7 +40483,7 @@ var Server = class extends Protocol {
|
|
|
40050
40483
|
};
|
|
40051
40484
|
|
|
40052
40485
|
// src/mcp/server.ts
|
|
40053
|
-
var
|
|
40486
|
+
var path49 = __toESM(require("path"));
|
|
40054
40487
|
|
|
40055
40488
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
40056
40489
|
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
@@ -40133,12 +40566,12 @@ var StdioServerTransport = class {
|
|
|
40133
40566
|
this.onclose?.();
|
|
40134
40567
|
}
|
|
40135
40568
|
send(message) {
|
|
40136
|
-
return new Promise((
|
|
40569
|
+
return new Promise((resolve36) => {
|
|
40137
40570
|
const json = serializeMessage(message);
|
|
40138
40571
|
if (this._stdout.write(json)) {
|
|
40139
|
-
|
|
40572
|
+
resolve36();
|
|
40140
40573
|
} else {
|
|
40141
|
-
this._stdout.once("drain",
|
|
40574
|
+
this._stdout.once("drain", resolve36);
|
|
40142
40575
|
}
|
|
40143
40576
|
});
|
|
40144
40577
|
}
|
|
@@ -40216,7 +40649,7 @@ var LaunchEnvironmentInputSchema = external_exports.object({
|
|
|
40216
40649
|
worktree: external_exports.boolean().default(true),
|
|
40217
40650
|
claude: external_exports.boolean().default(false),
|
|
40218
40651
|
confirmReplace: external_exports.boolean().default(false).describe(
|
|
40219
|
-
"
|
|
40652
|
+
"Destroy the previous session on this slot and start another from main-checkout HEAD. Does not choose main. Prefer complete/teardown when the prior task is finished. Call har_get_status first; get explicit user approval."
|
|
40220
40653
|
),
|
|
40221
40654
|
force: external_exports.boolean().default(false).describe(
|
|
40222
40655
|
"Discard uncommitted changes in a dirty occupied worktree. Requires confirmReplace=true and explicit user approval \u2014 never set autonomously."
|
|
@@ -40342,7 +40775,7 @@ var HAR_MCP_TOOLS = [
|
|
|
40342
40775
|
},
|
|
40343
40776
|
{
|
|
40344
40777
|
name: "har_launch_environment",
|
|
40345
|
-
description: "Start a FRESH agent session. Run
|
|
40778
|
+
description: "Start a FRESH agent session from the main checkout HEAD (switch that checkout to main first for a new unrelated task). Run BEFORE editing any file: returns workDir \u2014 ALL edits go there, never the main checkout. Prefer har_complete_environment / har_teardown_environment when a prior task is done. Occupied slots block until confirmReplace=true (does NOT choose main). force=true discards dirty work \u2014 never set without user approval. Failed launches: resume=true / har_recover_environment.",
|
|
40346
40779
|
inputSchema: objectJsonSchema(
|
|
40347
40780
|
{
|
|
40348
40781
|
repo: repoJsonProperty,
|
|
@@ -40351,7 +40784,7 @@ var HAR_MCP_TOOLS = [
|
|
|
40351
40784
|
claude: { type: "boolean" },
|
|
40352
40785
|
confirmReplace: {
|
|
40353
40786
|
type: "boolean",
|
|
40354
|
-
description: "
|
|
40787
|
+
description: "Destroy the previous session on this slot and start another from main-checkout HEAD. Does not choose main. Prefer complete/teardown when the prior task is finished. Call har_get_status first; get explicit user approval."
|
|
40355
40788
|
},
|
|
40356
40789
|
force: {
|
|
40357
40790
|
type: "boolean",
|
|
@@ -40359,7 +40792,7 @@ var HAR_MCP_TOOLS = [
|
|
|
40359
40792
|
},
|
|
40360
40793
|
resume: {
|
|
40361
40794
|
type: "boolean",
|
|
40362
|
-
description: "Resume a failed or partial launch (status failed/starting) without
|
|
40795
|
+
description: "Resume a failed or partial launch (status failed/starting) without confirmReplace. Preserves worktree and env."
|
|
40363
40796
|
}
|
|
40364
40797
|
},
|
|
40365
40798
|
["agentId"]
|
|
@@ -40682,7 +41115,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
40682
41115
|
const input = ControlUpInputSchema.parse({ ...args, repo });
|
|
40683
41116
|
const result = await startControlAndSync({
|
|
40684
41117
|
detach: input.detach,
|
|
40685
|
-
cwd:
|
|
41118
|
+
cwd: path49.resolve(input.repo)
|
|
40686
41119
|
});
|
|
40687
41120
|
if (result.code !== 0) {
|
|
40688
41121
|
return {
|
|
@@ -40745,7 +41178,7 @@ var mcpCommand = {
|
|
|
40745
41178
|
};
|
|
40746
41179
|
|
|
40747
41180
|
// src/cli/commands/telemetry.ts
|
|
40748
|
-
var
|
|
41181
|
+
var path50 = __toESM(require("path"));
|
|
40749
41182
|
init_control_config();
|
|
40750
41183
|
init_otel_hooks();
|
|
40751
41184
|
init_telemetry_config();
|
|
@@ -40830,7 +41263,7 @@ async function handleOff() {
|
|
|
40830
41263
|
info("Mission Control will not auto-start. Existing usage rows are kept.");
|
|
40831
41264
|
}
|
|
40832
41265
|
async function handleWriteEnv(argv) {
|
|
40833
|
-
const repoPath =
|
|
41266
|
+
const repoPath = path50.resolve(argv.repo);
|
|
40834
41267
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
40835
41268
|
const session = readSlotRegistry(harnessRoot, argv.agentId);
|
|
40836
41269
|
const workDir = argv.workDir ?? session?.workDir ?? repoPath;
|
|
@@ -40842,7 +41275,7 @@ async function handleWriteEnv(argv) {
|
|
|
40842
41275
|
suffix,
|
|
40843
41276
|
createdAt: session?.createdAt
|
|
40844
41277
|
});
|
|
40845
|
-
const envFile = argv.envFile ??
|
|
41278
|
+
const envFile = argv.envFile ?? path50.join(workDir, `.env.agent.${argv.agentId}`);
|
|
40846
41279
|
const attrs = {
|
|
40847
41280
|
sessionKey,
|
|
40848
41281
|
agentId: argv.agentId,
|
|
@@ -40861,7 +41294,7 @@ async function handleWriteEnv(argv) {
|
|
|
40861
41294
|
success(`Wrote session attribution to ${envFile}`);
|
|
40862
41295
|
}
|
|
40863
41296
|
function handlePrintEnv(argv) {
|
|
40864
|
-
const repoPath =
|
|
41297
|
+
const repoPath = path50.resolve(argv.repo);
|
|
40865
41298
|
const workDir = argv.workDir ?? repoPath;
|
|
40866
41299
|
const sessionKey = buildSessionKey({
|
|
40867
41300
|
branch: argv.branch,
|
|
@@ -40974,7 +41407,7 @@ var telemetryCommand = {
|
|
|
40974
41407
|
|
|
40975
41408
|
// src/cli/index.ts
|
|
40976
41409
|
async function runCli() {
|
|
40977
|
-
await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(envCommand).command(agentsCommand).command(controlCommand).command(hooksCommand).command(mcpCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har env
|
|
41410
|
+
await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(envCommand).command(agentsCommand).command(controlCommand).command(hooksCommand).command(mcpCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har env --help").epilog(HAR_ROOT_EPILOG).strict().help().version(getHarPackageVersion()).parse();
|
|
40978
41411
|
}
|
|
40979
41412
|
|
|
40980
41413
|
// src/index.ts
|