@tablation/crew 0.126.1 → 0.126.3
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/cli.js +245 -117
- package/dist/sandbox-intel-bridge.cjs +29 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -794,7 +794,7 @@ function shellInitScript(shell) {
|
|
|
794
794
|
|
|
795
795
|
// src/config.ts
|
|
796
796
|
import { readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
|
|
797
|
-
import { resolve as resolve2, dirname as
|
|
797
|
+
import { resolve as resolve2, dirname as dirname8, isAbsolute as isAbsolute2, join as join17, basename as basename4 } from "path";
|
|
798
798
|
import { homedir as homedir6, tmpdir } from "os";
|
|
799
799
|
import { parse as parse2 } from "yaml";
|
|
800
800
|
|
|
@@ -2661,7 +2661,7 @@ function explain(ship, required) {
|
|
|
2661
2661
|
|
|
2662
2662
|
// src/repo-config.ts
|
|
2663
2663
|
import { readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
|
|
2664
|
-
import { join as join16, basename as basename3 } from "path";
|
|
2664
|
+
import { join as join16, basename as basename3, dirname as dirname7 } from "path";
|
|
2665
2665
|
import { parse } from "yaml";
|
|
2666
2666
|
|
|
2667
2667
|
// src/sandbox-policy.ts
|
|
@@ -2863,6 +2863,19 @@ var SPEC_VERSION = 1;
|
|
|
2863
2863
|
var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
|
|
2864
2864
|
var RepoConfigError = class extends Error {
|
|
2865
2865
|
};
|
|
2866
|
+
function versionFileProblem(name, text2) {
|
|
2867
|
+
const remedy = `a version file that is not a JSON object with a string "version" needs hooks.bump instead of release.versionFiles (docs/REPO_SPEC.md)`;
|
|
2868
|
+
let doc;
|
|
2869
|
+
try {
|
|
2870
|
+
doc = JSON.parse(text2);
|
|
2871
|
+
} catch {
|
|
2872
|
+
return `release.versionFiles entry "${name}" is not a JSON document \u2014 ${remedy}`;
|
|
2873
|
+
}
|
|
2874
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc) || typeof doc.version !== "string") {
|
|
2875
|
+
return `release.versionFiles entry "${name}" has no string "version" field \u2014 ${remedy}`;
|
|
2876
|
+
}
|
|
2877
|
+
return null;
|
|
2878
|
+
}
|
|
2866
2879
|
var HOOK_NAMES = [
|
|
2867
2880
|
"test",
|
|
2868
2881
|
"build",
|
|
@@ -3197,6 +3210,22 @@ function parseRepoConfig(text2, file) {
|
|
|
3197
3210
|
if (versionFiles.length === 0) {
|
|
3198
3211
|
throw new RepoConfigError(`${file}: release.versionFiles cannot be empty`);
|
|
3199
3212
|
}
|
|
3213
|
+
if (versionFilesGiven) {
|
|
3214
|
+
for (const name of versionFiles) {
|
|
3215
|
+
const path = join16(dirname7(file), name);
|
|
3216
|
+
if (!existsSync14(path)) continue;
|
|
3217
|
+
let text3;
|
|
3218
|
+
try {
|
|
3219
|
+
text3 = readFileSync14(path, "utf8");
|
|
3220
|
+
} catch (e) {
|
|
3221
|
+
throw new RepoConfigError(
|
|
3222
|
+
`${file}: release.versionFiles entry "${name}" cannot be read (${e.code ?? "read error"})`
|
|
3223
|
+
);
|
|
3224
|
+
}
|
|
3225
|
+
const problem = versionFileProblem(name, text3);
|
|
3226
|
+
if (problem) throw new RepoConfigError(`${file}: ${problem}`);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3200
3229
|
const inspectorRaw = raw.periscope?.inspector;
|
|
3201
3230
|
if (inspectorRaw !== void 0 && inspectorRaw !== "proxy" && inspectorRaw !== "repo") {
|
|
3202
3231
|
throw new RepoConfigError(`${file}: periscope.inspector must be "proxy" or "repo"`);
|
|
@@ -3234,6 +3263,15 @@ function loadRepoConfig(dir) {
|
|
|
3234
3263
|
if (!file) return null;
|
|
3235
3264
|
return parseRepoConfig(readFileSync14(file, "utf8"), file);
|
|
3236
3265
|
}
|
|
3266
|
+
function loadRepoConfigTolerant(dir, refused) {
|
|
3267
|
+
try {
|
|
3268
|
+
return loadRepoConfig(dir);
|
|
3269
|
+
} catch (e) {
|
|
3270
|
+
if (!(e instanceof RepoConfigError)) throw e;
|
|
3271
|
+
refused?.(e);
|
|
3272
|
+
return null;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3237
3275
|
function hookLabel(cfg, hook2) {
|
|
3238
3276
|
return cfg.labels[hook2] ?? cfg.hooks[hook2] ?? hook2;
|
|
3239
3277
|
}
|
|
@@ -3898,7 +3936,7 @@ function loadConfig(crewHome, configFile, opts = {}) {
|
|
|
3898
3936
|
` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
|
|
3899
3937
|
|
|
3900
3938
|
Create the first of those:
|
|
3901
|
-
mkdir -p ${
|
|
3939
|
+
mkdir -p ${dirname8(defaultConfigPath())} && cp ${join17(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
|
|
3902
3940
|
);
|
|
3903
3941
|
}
|
|
3904
3942
|
let raw;
|
|
@@ -3908,7 +3946,7 @@ Create the first of those:
|
|
|
3908
3946
|
throw new ConfigError(`${file}: not valid YAML \u2014 ${e.message}`);
|
|
3909
3947
|
}
|
|
3910
3948
|
if (!raw || typeof raw !== "object") throw new ConfigError(`${file} is empty or not a mapping`);
|
|
3911
|
-
const base =
|
|
3949
|
+
const base = dirname8(file);
|
|
3912
3950
|
const missing = new Missing();
|
|
3913
3951
|
rejectUnknownKeys(raw, /* @__PURE__ */ new Set(["ship", "routes"]), "top-level", file);
|
|
3914
3952
|
const shipRaw = raw.ship ?? {};
|
|
@@ -4275,7 +4313,7 @@ function locateDir(cfg, dir, commonDirOf) {
|
|
|
4275
4313
|
const direct = find(target);
|
|
4276
4314
|
if (direct) return direct;
|
|
4277
4315
|
const common = commonDirOf?.(target);
|
|
4278
|
-
return common ? find(
|
|
4316
|
+
return common ? find(dirname8(resolve2(target, common))) : void 0;
|
|
4279
4317
|
}
|
|
4280
4318
|
function findRoute(cfg, route2) {
|
|
4281
4319
|
if (!route2) {
|
|
@@ -4717,7 +4755,7 @@ function recoveredFailureHeadlines(o, route2) {
|
|
|
4717
4755
|
}
|
|
4718
4756
|
|
|
4719
4757
|
// src/install.ts
|
|
4720
|
-
import { dirname as
|
|
4758
|
+
import { dirname as dirname9, join as join18, extname } from "path";
|
|
4721
4759
|
import { homedir as homedir7, userInfo } from "os";
|
|
4722
4760
|
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync5, writeFileSync as writeFileSync12, unlinkSync, realpathSync } from "fs";
|
|
4723
4761
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
@@ -4762,7 +4800,7 @@ function schedulerLogFor(crewLog, job) {
|
|
|
4762
4800
|
return `${stem}.${suffix}${ext || ".log"}`;
|
|
4763
4801
|
}
|
|
4764
4802
|
function pathFor(ship) {
|
|
4765
|
-
const nodeBinDir =
|
|
4803
|
+
const nodeBinDir = dirname9(process.execPath);
|
|
4766
4804
|
const parts = [nodeBinDir];
|
|
4767
4805
|
if (ship.extraPath) parts.push(ship.extraPath);
|
|
4768
4806
|
parts.push("/usr/bin", "/bin", "/usr/sbin", "/sbin");
|
|
@@ -4784,7 +4822,7 @@ function dockerPathProblem(ship, shellPath = process.env.PATH ?? "") {
|
|
|
4784
4822
|
const schedulerPath = pathFor(ship);
|
|
4785
4823
|
if (findOnPath("docker", schedulerPath)) return void 0;
|
|
4786
4824
|
const inShell = findOnPath("docker", shellPath);
|
|
4787
|
-
return inShell ? `\`docker\` is not on the scheduler PATH (${schedulerPath}) but is at ${inShell} in your shell; add ${
|
|
4825
|
+
return inShell ? `\`docker\` is not on the scheduler PATH (${schedulerPath}) but is at ${inShell} in your shell; add ${dirname9(inShell)} to ship.extraPath in crew.yaml and run \`crew install\`` : `\`docker\` is not on the scheduler PATH (${schedulerPath}) or your shell PATH; install Docker, then add its directory to ship.extraPath in crew.yaml and run \`crew install\``;
|
|
4788
4826
|
}
|
|
4789
4827
|
var USER_INSTALL = { root: false, user: "" };
|
|
4790
4828
|
function installContext(env = process.env) {
|
|
@@ -5079,7 +5117,7 @@ async function applyInstall(plan, crewHome, dryRun, log) {
|
|
|
5079
5117
|
}
|
|
5080
5118
|
const before = existsSync16(path) ? readFileSync16(path, "utf8") : null;
|
|
5081
5119
|
if (before !== plan.unitContent[path]) changed2 = true;
|
|
5082
|
-
mkdirSync11(
|
|
5120
|
+
mkdirSync11(dirname9(path), { recursive: true });
|
|
5083
5121
|
writeFileSync12(path, plan.unitContent[path]);
|
|
5084
5122
|
log.emit(`wrote ${path}`);
|
|
5085
5123
|
}
|
|
@@ -5308,7 +5346,7 @@ import {
|
|
|
5308
5346
|
readdirSync as readdirSync4,
|
|
5309
5347
|
rmSync as rmSync9
|
|
5310
5348
|
} from "fs";
|
|
5311
|
-
import { dirname as
|
|
5349
|
+
import { dirname as dirname10, basename as basename5, join as join19 } from "path";
|
|
5312
5350
|
var GitError = class extends Error {
|
|
5313
5351
|
};
|
|
5314
5352
|
function streamText(x) {
|
|
@@ -5688,7 +5726,7 @@ function recoverStrandedReleaseCheckouts(cwd) {
|
|
|
5688
5726
|
if (line.startsWith("worktree ")) path = line.slice("worktree ".length);
|
|
5689
5727
|
else if (line.startsWith("branch refs/heads/crew/release-") && path) {
|
|
5690
5728
|
gitOk(cwd, ["worktree", "remove", "--force", path]);
|
|
5691
|
-
const root =
|
|
5729
|
+
const root = dirname10(path);
|
|
5692
5730
|
if (basename5(root).startsWith("crew-release-")) rmSync9(root, { recursive: true, force: true });
|
|
5693
5731
|
}
|
|
5694
5732
|
}
|
|
@@ -5768,7 +5806,7 @@ function ensureRepoCheckout(dir, remote, opts = {}) {
|
|
|
5768
5806
|
`${dir} does not exist and this repo has no discovered remote to clone from \u2014 re-run \`crew connect\`, or check out the repository yourself and point \`repos:\` at it`
|
|
5769
5807
|
);
|
|
5770
5808
|
}
|
|
5771
|
-
mkdirSync12(
|
|
5809
|
+
mkdirSync12(dirname10(dir), { recursive: true });
|
|
5772
5810
|
const url = cloneUrlFor(remote, opts);
|
|
5773
5811
|
try {
|
|
5774
5812
|
execFileSync5("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -5793,7 +5831,7 @@ function sleepSync(ms3) {
|
|
|
5793
5831
|
}
|
|
5794
5832
|
function acquireCheckoutLock(dir, timeoutMs = 3e4) {
|
|
5795
5833
|
const lockPath = checkoutLockPath(dir);
|
|
5796
|
-
mkdirSync12(
|
|
5834
|
+
mkdirSync12(dirname10(lockPath), { recursive: true });
|
|
5797
5835
|
const deadline = Date.now() + timeoutMs;
|
|
5798
5836
|
for (; ; ) {
|
|
5799
5837
|
try {
|
|
@@ -5987,6 +6025,15 @@ function seatKeyUnreadableItem(route2, seat, storeError, nowIso = (/* @__PURE__
|
|
|
5987
6025
|
message: `${route2}: the ${seat} seat's agent key cannot be read (${storeError}) \u2014 its runs are refused until the key store is readable. Resumes automatically once it is; \`crew doctor\` names the store in use`
|
|
5988
6026
|
};
|
|
5989
6027
|
}
|
|
6028
|
+
var repoContractRefusedKey = (repo) => `repo_contract_refused:${repo}`;
|
|
6029
|
+
function repoContractRefusedItem(repo, detail, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
6030
|
+
return {
|
|
6031
|
+
kind: "repo_contract_refused",
|
|
6032
|
+
key: repoContractRefusedKey(repo),
|
|
6033
|
+
since: nowIso,
|
|
6034
|
+
message: `${repo}: its repo contract is refused and the repository is skipped \u2014 ${detail}. Resumes automatically once the file is valid on its base; \`crew doctor\` names the problem`
|
|
6035
|
+
};
|
|
6036
|
+
}
|
|
5990
6037
|
var agentLogRefusedKey = (route2) => `agent_log_refused:${route2}`;
|
|
5991
6038
|
function agentLogRefusedItem(route2, role, status2, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
5992
6039
|
return {
|
|
@@ -6100,7 +6147,7 @@ function recheckBaseStops(state, route2) {
|
|
|
6100
6147
|
for (const item of open) {
|
|
6101
6148
|
const target = reposOf(route2).find((t) => baseUnsafeKey(t.name) === item.key);
|
|
6102
6149
|
if (!target) continue;
|
|
6103
|
-
const cfg = resolveRepoConfig(
|
|
6150
|
+
const cfg = resolveRepoConfig(loadRepoConfigTolerant(target.dir), void 0, target.dir);
|
|
6104
6151
|
const outcome = refreshBaseBranch(target.dir, cfg.branch.remote, cfg.branch.base, true);
|
|
6105
6152
|
if (outcome.action === "level" || outcome.action === "would-fast-forward") {
|
|
6106
6153
|
clearShipAttention(state, item.key);
|
|
@@ -6305,7 +6352,7 @@ import { createHash as createHash6 } from "crypto";
|
|
|
6305
6352
|
import { spawn as spawn5 } from "child_process";
|
|
6306
6353
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
6307
6354
|
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
|
|
6308
|
-
import { dirname as
|
|
6355
|
+
import { dirname as dirname15, join as join32 } from "path";
|
|
6309
6356
|
import { fileURLToPath } from "url";
|
|
6310
6357
|
import { tmpdir as tmpdir3 } from "os";
|
|
6311
6358
|
|
|
@@ -6330,7 +6377,7 @@ import { spawnSync as spawnSync6 } from "child_process";
|
|
|
6330
6377
|
import { readFileSync as readFileSync25, existsSync as existsSync26, statSync as statSync8, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
|
|
6331
6378
|
import { StringDecoder } from "string_decoder";
|
|
6332
6379
|
import { finished } from "stream/promises";
|
|
6333
|
-
import { join as join29, dirname as
|
|
6380
|
+
import { join as join29, dirname as dirname14, basename as basename9 } from "path";
|
|
6334
6381
|
import { createHash as createHash4 } from "crypto";
|
|
6335
6382
|
|
|
6336
6383
|
// src/state.ts
|
|
@@ -7235,7 +7282,7 @@ function renderEnvironment(i) {
|
|
|
7235
7282
|
|
|
7236
7283
|
// src/intel-bridge.ts
|
|
7237
7284
|
import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
|
|
7238
|
-
import { dirname as
|
|
7285
|
+
import { dirname as dirname11, join as join22 } from "path";
|
|
7239
7286
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7240
7287
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7241
7288
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -7255,7 +7302,7 @@ function intelDir(stateDir, route2, ticket) {
|
|
|
7255
7302
|
}
|
|
7256
7303
|
var attachFilePath = (stateDir, route2, ticket) => join22(intelDir(stateDir, route2, ticket), "url");
|
|
7257
7304
|
function writeAttachFile(file, url) {
|
|
7258
|
-
mkdirSync14(
|
|
7305
|
+
mkdirSync14(dirname11(file), { recursive: true });
|
|
7259
7306
|
writeFileSync15(file, `${url}
|
|
7260
7307
|
`);
|
|
7261
7308
|
}
|
|
@@ -9300,7 +9347,7 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
|
|
|
9300
9347
|
|
|
9301
9348
|
// src/run-worktree.ts
|
|
9302
9349
|
import { existsSync as existsSync22 } from "fs";
|
|
9303
|
-
import { dirname as
|
|
9350
|
+
import { dirname as dirname12, join as join24 } from "path";
|
|
9304
9351
|
var describeRunWorktreePlan = (p) => {
|
|
9305
9352
|
switch (p.action) {
|
|
9306
9353
|
case "cut-remote":
|
|
@@ -9357,7 +9404,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
|
|
|
9357
9404
|
if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
|
|
9358
9405
|
return { action: "recut", path: existing.path, branch, base: baseRef };
|
|
9359
9406
|
}
|
|
9360
|
-
const path = join24(
|
|
9407
|
+
const path = join24(dirname12(dir), worktreeForTicket(dir, cfg, t, role));
|
|
9361
9408
|
if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
|
|
9362
9409
|
return { action: "cut-remote", path, branch: located.branch };
|
|
9363
9410
|
}
|
|
@@ -10631,7 +10678,7 @@ function skillMarkerWarnings(promptsDir, personaPrompts) {
|
|
|
10631
10678
|
import { spawn as spawn3, execFile, execFileSync as execFileSync6 } from "child_process";
|
|
10632
10679
|
import { mkdtempSync, mkdirSync as mkdirSync17, writeFileSync as writeFileSync18, rmSync as rmSync14 } from "fs";
|
|
10633
10680
|
import { tmpdir as tmpdir2 } from "os";
|
|
10634
|
-
import { basename as basename8, dirname as
|
|
10681
|
+
import { basename as basename8, dirname as dirname13, join as join28 } from "path";
|
|
10635
10682
|
import { promisify } from "util";
|
|
10636
10683
|
|
|
10637
10684
|
// src/run-limit.ts
|
|
@@ -10760,7 +10807,7 @@ var SandboxRunner = class {
|
|
|
10760
10807
|
throw new Error(`cannot generate the sandbox policy: ${e.message}`);
|
|
10761
10808
|
}
|
|
10762
10809
|
policyFile = policyPathFor(plan.streamPath);
|
|
10763
|
-
mkdirSync17(
|
|
10810
|
+
mkdirSync17(dirname13(policyFile), { recursive: true });
|
|
10764
10811
|
writeFileSync18(policyFile, generated.yaml, { mode: 384 });
|
|
10765
10812
|
}
|
|
10766
10813
|
const create2 = ["sandbox", "create", "--name", name, "--detach"];
|
|
@@ -11022,7 +11069,7 @@ function codeBridgeFor(o, harness) {
|
|
|
11022
11069
|
"--clone",
|
|
11023
11070
|
o.ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR,
|
|
11024
11071
|
"--state-dir",
|
|
11025
|
-
|
|
11072
|
+
dirname14(SANDBOX_INTEL_DIR),
|
|
11026
11073
|
"--ticket",
|
|
11027
11074
|
number,
|
|
11028
11075
|
...seed ? ["--seed-cache", seed] : []
|
|
@@ -11036,7 +11083,7 @@ function codeBridgeFor(o, harness) {
|
|
|
11036
11083
|
bridge.files = [{ path: bridge.settingsFile, content: text2 }];
|
|
11037
11084
|
} else {
|
|
11038
11085
|
const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
|
|
11039
|
-
mkdirSync18(
|
|
11086
|
+
mkdirSync18(dirname14(file), { recursive: true });
|
|
11040
11087
|
writeFileSync19(file, text2);
|
|
11041
11088
|
bridge.settingsFile = file;
|
|
11042
11089
|
}
|
|
@@ -11259,7 +11306,7 @@ async function killForDeadline(kill, pid, emit, role, ticket, spawnedAt, limitMi
|
|
|
11259
11306
|
var withoutKey = ({ apiKey: _apiKey, ...rest }) => rest;
|
|
11260
11307
|
function openSink(path) {
|
|
11261
11308
|
try {
|
|
11262
|
-
mkdirSync18(
|
|
11309
|
+
mkdirSync18(dirname14(path), { recursive: true });
|
|
11263
11310
|
return createWriteStream(path, { flags: "a" });
|
|
11264
11311
|
} catch {
|
|
11265
11312
|
return void 0;
|
|
@@ -11611,7 +11658,7 @@ function spawnAgent(plan, emit, hintMoved) {
|
|
|
11611
11658
|
const id = identityOf(plan, adapter.harness);
|
|
11612
11659
|
let streamFd;
|
|
11613
11660
|
try {
|
|
11614
|
-
mkdirSync18(
|
|
11661
|
+
mkdirSync18(dirname14(plan.streamPath), { recursive: true });
|
|
11615
11662
|
streamFd = openSync4(plan.streamPath, "a");
|
|
11616
11663
|
} catch (e) {
|
|
11617
11664
|
void endSink(eventsSink);
|
|
@@ -12776,7 +12823,7 @@ function syncPassengerContainers(cfg, tablationApiBaseUrl, exec = realExec2, syn
|
|
|
12776
12823
|
}
|
|
12777
12824
|
return { started, recreated, stopped };
|
|
12778
12825
|
}
|
|
12779
|
-
function passengerCheckoutSyncSpecs(cfg, workspaceId) {
|
|
12826
|
+
function passengerCheckoutSyncSpecs(cfg, workspaceId, onRefused) {
|
|
12780
12827
|
const specs = [];
|
|
12781
12828
|
for (const route2 of cfg.routes) {
|
|
12782
12829
|
if (!route2.enabled || !route2.hostPassengers || !route2.resolved?.workspaceId) continue;
|
|
@@ -12788,8 +12835,14 @@ function passengerCheckoutSyncSpecs(cfg, workspaceId) {
|
|
|
12788
12835
|
for (const target of passengerCheckoutTargets(route2, mirrorArea)) {
|
|
12789
12836
|
const ordinary = ordinaryByName.get(target.name);
|
|
12790
12837
|
const override = route2.repoOverrides[target.name];
|
|
12838
|
+
let refused = false;
|
|
12839
|
+
const declared = ordinary ? loadRepoConfigTolerant(ordinary.dir, (e) => {
|
|
12840
|
+
refused = true;
|
|
12841
|
+
onRefused?.(target.name, e);
|
|
12842
|
+
}) : null;
|
|
12843
|
+
if (refused) continue;
|
|
12791
12844
|
const effective = resolveRepoConfig(
|
|
12792
|
-
|
|
12845
|
+
declared,
|
|
12793
12846
|
{ branch: { ...route2.branch, ...override?.branch } },
|
|
12794
12847
|
ordinary?.dir ?? target.dir
|
|
12795
12848
|
);
|
|
@@ -12809,8 +12862,8 @@ function passengerCheckoutSyncSpecs(cfg, workspaceId) {
|
|
|
12809
12862
|
}
|
|
12810
12863
|
return specs;
|
|
12811
12864
|
}
|
|
12812
|
-
function syncAllPassengerCheckouts(cfg) {
|
|
12813
|
-
return passengerCheckoutSyncSpecs(cfg).filter((spec) => !spec.mirror).map((spec) => ({
|
|
12865
|
+
function syncAllPassengerCheckouts(cfg, onRefused) {
|
|
12866
|
+
return passengerCheckoutSyncSpecs(cfg, void 0, onRefused).filter((spec) => !spec.mirror).map((spec) => ({
|
|
12814
12867
|
name: spec.name,
|
|
12815
12868
|
workspaceId: spec.workspaceId,
|
|
12816
12869
|
outcome: syncPassengerCheckout(spec.dir, spec.remote, spec.gitRemoteName, spec.base, { originUrl: spec.originUrl, scheme: spec.cloneScheme, host: spec.forgeHost })
|
|
@@ -12830,7 +12883,7 @@ var SANDBOX_UID = 1e3;
|
|
|
12830
12883
|
var CREW_PREFIX = "/opt/crew";
|
|
12831
12884
|
var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
|
|
12832
12885
|
function readBridgeBundle() {
|
|
12833
|
-
const here =
|
|
12886
|
+
const here = dirname15(fileURLToPath(import.meta.url));
|
|
12834
12887
|
for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
|
|
12835
12888
|
if (existsSync30(p)) return readFileSync27(p, "utf8");
|
|
12836
12889
|
}
|
|
@@ -13055,7 +13108,7 @@ async function buildSandboxImage(plan, deps) {
|
|
|
13055
13108
|
try {
|
|
13056
13109
|
writeFileSync20(join32(ctx, "Dockerfile"), layer.dockerfile);
|
|
13057
13110
|
for (const [name, content] of Object.entries(layer.files)) {
|
|
13058
|
-
mkdirSync19(
|
|
13111
|
+
mkdirSync19(dirname15(join32(ctx, name)), { recursive: true });
|
|
13059
13112
|
writeFileSync20(join32(ctx, name), content);
|
|
13060
13113
|
}
|
|
13061
13114
|
const code = await deps.run(["build", "--tag", layer.tag, "--file", join32(ctx, "Dockerfile"), ctx], logPath);
|
|
@@ -13468,7 +13521,7 @@ function doctorSelftestLines(stored, toolchains) {
|
|
|
13468
13521
|
}
|
|
13469
13522
|
|
|
13470
13523
|
// src/cli.ts
|
|
13471
|
-
import { resolve as resolve7, dirname as
|
|
13524
|
+
import { resolve as resolve7, dirname as dirname34, join as join56, basename as basename16 } from "path";
|
|
13472
13525
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13473
13526
|
import { hostname as hostname4, homedir as homedir11 } from "os";
|
|
13474
13527
|
import { spawnSync as spawnSync11 } from "child_process";
|
|
@@ -13667,7 +13720,7 @@ function rosterMarkdown(roster, meId) {
|
|
|
13667
13720
|
|
|
13668
13721
|
// src/connect-resume.ts
|
|
13669
13722
|
import { mkdirSync as mkdirSync21, readFileSync as readFileSync29, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
|
|
13670
|
-
import { dirname as
|
|
13723
|
+
import { dirname as dirname16, join as join34 } from "path";
|
|
13671
13724
|
function lastSessionFile(stateDir) {
|
|
13672
13725
|
return join34(stateDir, "connect-session.json");
|
|
13673
13726
|
}
|
|
@@ -13687,7 +13740,7 @@ function readPointers(stateDir) {
|
|
|
13687
13740
|
}
|
|
13688
13741
|
function writePointers(stateDir, pointers) {
|
|
13689
13742
|
const file = lastSessionFile(stateDir);
|
|
13690
|
-
mkdirSync21(
|
|
13743
|
+
mkdirSync21(dirname16(file), { recursive: true });
|
|
13691
13744
|
writeFileSync22(file, `${JSON.stringify(pointers, null, 2)}
|
|
13692
13745
|
`, { mode: 384 });
|
|
13693
13746
|
}
|
|
@@ -13774,7 +13827,7 @@ async function signOutSessions(o) {
|
|
|
13774
13827
|
|
|
13775
13828
|
// src/reset.ts
|
|
13776
13829
|
import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
|
|
13777
|
-
import { dirname as
|
|
13830
|
+
import { dirname as dirname17, join as join35 } from "path";
|
|
13778
13831
|
import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
|
|
13779
13832
|
function clonedCheckoutsFile(stateDir) {
|
|
13780
13833
|
return join35(stateDir, "cloned-checkouts.json");
|
|
@@ -13791,7 +13844,7 @@ function recordClonedCheckout(stateDir, dir) {
|
|
|
13791
13844
|
const known = readClonedCheckouts(stateDir);
|
|
13792
13845
|
if (known.includes(dir)) return;
|
|
13793
13846
|
const file = clonedCheckoutsFile(stateDir);
|
|
13794
|
-
mkdirSync22(
|
|
13847
|
+
mkdirSync22(dirname17(file), { recursive: true });
|
|
13795
13848
|
writeFileSync23(file, `${JSON.stringify([...known, dir], null, 2)}
|
|
13796
13849
|
`);
|
|
13797
13850
|
}
|
|
@@ -13974,7 +14027,7 @@ async function runDeprovision(o, fx, deps) {
|
|
|
13974
14027
|
// src/pool-member.ts
|
|
13975
14028
|
import { mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
|
|
13976
14029
|
import { homedir as homedir8 } from "os";
|
|
13977
|
-
import { dirname as
|
|
14030
|
+
import { dirname as dirname18, join as join36 } from "path";
|
|
13978
14031
|
var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
|
|
13979
14032
|
function backoffMs(attempt) {
|
|
13980
14033
|
return Math.min(6e4, 5e3 * 2 ** Math.max(0, attempt));
|
|
@@ -14218,7 +14271,7 @@ function fileMemberStore(dir) {
|
|
|
14218
14271
|
}
|
|
14219
14272
|
},
|
|
14220
14273
|
write(f) {
|
|
14221
|
-
mkdirSync23(
|
|
14274
|
+
mkdirSync23(dirname18(file), { recursive: true, mode: 448 });
|
|
14222
14275
|
const tmp = `${file}.tmp`;
|
|
14223
14276
|
writeFileSync24(tmp, `${JSON.stringify(f)}
|
|
14224
14277
|
`, { mode: 384 });
|
|
@@ -14279,7 +14332,7 @@ import { hostname as hostname3 } from "os";
|
|
|
14279
14332
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
14280
14333
|
import { createHash as createHash7 } from "crypto";
|
|
14281
14334
|
import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
|
|
14282
|
-
import { dirname as
|
|
14335
|
+
import { dirname as dirname19, join as join37 } from "path";
|
|
14283
14336
|
function machineIdPathFor(stateDir) {
|
|
14284
14337
|
return join37(stateDir, "machine-id");
|
|
14285
14338
|
}
|
|
@@ -14313,7 +14366,7 @@ function ensureMachineId(stateDir, opts = {}) {
|
|
|
14313
14366
|
const raw = (opts.read ?? readRawMachineId)();
|
|
14314
14367
|
if (!raw) return void 0;
|
|
14315
14368
|
const hashed = sha256(raw);
|
|
14316
|
-
mkdirSync24(
|
|
14369
|
+
mkdirSync24(dirname19(path), { recursive: true });
|
|
14317
14370
|
writeFileSync25(path, `${hashed}
|
|
14318
14371
|
`, { mode: 384 });
|
|
14319
14372
|
return hashed;
|
|
@@ -16425,7 +16478,7 @@ function writeDigest(o, d, role, stateDir, runId, own) {
|
|
|
16425
16478
|
if (hit) return hit;
|
|
16426
16479
|
const target = reposOf(o.route).find((r) => r.dir === dir);
|
|
16427
16480
|
const nameOverride = target ? o.route.repoOverrides[target.name]?.worktreeName : void 0;
|
|
16428
|
-
const cfg = resolveRepoConfig(
|
|
16481
|
+
const cfg = resolveRepoConfig(loadRepoConfigTolerant(dir), nameOverride ? { worktrees: { nameOverride } } : void 0, dir);
|
|
16429
16482
|
repoCache.set(dir, cfg);
|
|
16430
16483
|
return cfg;
|
|
16431
16484
|
};
|
|
@@ -16594,7 +16647,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
|
|
|
16594
16647
|
|
|
16595
16648
|
// src/repo-refresh.ts
|
|
16596
16649
|
import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
|
|
16597
|
-
import { dirname as
|
|
16650
|
+
import { dirname as dirname21 } from "path";
|
|
16598
16651
|
var REFRESH_COOLDOWN_MS = 2 * 6e4;
|
|
16599
16652
|
var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
|
|
16600
16653
|
var lastAsked = /* @__PURE__ */ new Map();
|
|
@@ -16626,7 +16679,7 @@ function persistRepoRows(stateDir, routeName, rows) {
|
|
|
16626
16679
|
if (!existsSync37(path)) return;
|
|
16627
16680
|
const raw = JSON.parse(readFileSync35(path, "utf8"));
|
|
16628
16681
|
applyRepoRows(raw, rows);
|
|
16629
|
-
mkdirSync26(
|
|
16682
|
+
mkdirSync26(dirname21(path), { recursive: true });
|
|
16630
16683
|
writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
|
|
16631
16684
|
`);
|
|
16632
16685
|
}
|
|
@@ -17077,7 +17130,7 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
|
|
|
17077
17130
|
var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
|
|
17078
17131
|
|
|
17079
17132
|
// src/connect-wizard.ts
|
|
17080
|
-
import { delimiter, dirname as
|
|
17133
|
+
import { delimiter, dirname as dirname22, join as join41 } from "path";
|
|
17081
17134
|
import { accessSync as accessSync3, constants as constants3, existsSync as existsSync38, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
|
|
17082
17135
|
import { stringify as stringify3 } from "yaml";
|
|
17083
17136
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
@@ -17126,9 +17179,9 @@ async function runWizard(p, env) {
|
|
|
17126
17179
|
if (entered && isExec(entered)) agentBin = entered;
|
|
17127
17180
|
else p.say(entered ? `${entered} is not an executable file.` : "A path is required.");
|
|
17128
17181
|
}
|
|
17129
|
-
const nodeDir =
|
|
17182
|
+
const nodeDir = dirname22(env.nodePath);
|
|
17130
17183
|
const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
|
|
17131
|
-
const baseDirs = [nodeDir, pnpmPath ?
|
|
17184
|
+
const baseDirs = [nodeDir, pnpmPath ? dirname22(pnpmPath) : void 0];
|
|
17132
17185
|
p.say(
|
|
17133
17186
|
`
|
|
17134
17187
|
Host Passengers lets this machine run the agent sessions that other people's
|
|
@@ -17139,7 +17192,7 @@ Tablation workspaces ask for, in a container. More: ${HOST_PASSENGERS_DOCS_URL}`
|
|
|
17139
17192
|
p.say("Docker is not available here, so nothing will be hosted until it is. Your answer is recorded anyway.");
|
|
17140
17193
|
}
|
|
17141
17194
|
const dockerPath = hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
|
|
17142
|
-
const extraPath = deriveExtraPath([...baseDirs, dockerPath ?
|
|
17195
|
+
const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname22(dockerPath) : void 0]);
|
|
17143
17196
|
if (extraPath) {
|
|
17144
17197
|
p.say(`Hooks will get ${extraPath} on their PATH (where node, pnpm${dockerPath ? " and docker" : ""} live).`);
|
|
17145
17198
|
}
|
|
@@ -17161,7 +17214,7 @@ routes:
|
|
|
17161
17214
|
${routeBlock}`;
|
|
17162
17215
|
}
|
|
17163
17216
|
function writeNewConfig(path, text2) {
|
|
17164
|
-
mkdirSync27(
|
|
17217
|
+
mkdirSync27(dirname22(path), { recursive: true });
|
|
17165
17218
|
try {
|
|
17166
17219
|
writeFileSync30(path, text2, { mode: 384, flag: "wx" });
|
|
17167
17220
|
return true;
|
|
@@ -17197,7 +17250,7 @@ Wrote ${o.configPath}.
|
|
|
17197
17250
|
// src/connect-json.ts
|
|
17198
17251
|
import { accessSync as accessSync4, constants as constants4 } from "fs";
|
|
17199
17252
|
import { homedir as homedir10 } from "os";
|
|
17200
|
-
import { dirname as
|
|
17253
|
+
import { dirname as dirname23, join as join42 } from "path";
|
|
17201
17254
|
import { delimiter as delimiter2 } from "path";
|
|
17202
17255
|
var CHOICE_EXIT_CODE = 3;
|
|
17203
17256
|
function createEmitter(json2, write) {
|
|
@@ -17263,7 +17316,7 @@ function flagWizardAnswers(f, env) {
|
|
|
17263
17316
|
const agentBin = f.agentBin ?? found?.bin;
|
|
17264
17317
|
const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
|
|
17265
17318
|
const dockerPath = f.hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
|
|
17266
|
-
const extraPath = f.extraPath ?? deriveExtraPath([
|
|
17319
|
+
const extraPath = f.extraPath ?? deriveExtraPath([dirname23(env.nodePath), pnpmPath ? dirname23(pnpmPath) : void 0, dockerPath ? dirname23(dockerPath) : void 0]);
|
|
17267
17320
|
return {
|
|
17268
17321
|
answers: {
|
|
17269
17322
|
name: f.shipName?.trim() || env.hostname,
|
|
@@ -17278,7 +17331,7 @@ function flagWizardAnswers(f, env) {
|
|
|
17278
17331
|
|
|
17279
17332
|
// src/release-run.ts
|
|
17280
17333
|
import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
|
|
17281
|
-
import { dirname as
|
|
17334
|
+
import { dirname as dirname24, join as join43 } from "path";
|
|
17282
17335
|
import { tmpdir as tmpdir5 } from "os";
|
|
17283
17336
|
|
|
17284
17337
|
// src/release.ts
|
|
@@ -18025,7 +18078,13 @@ async function bump(o, size) {
|
|
|
18025
18078
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
18026
18079
|
return null;
|
|
18027
18080
|
}
|
|
18028
|
-
const
|
|
18081
|
+
const firstText = readFileSync36(first, "utf8");
|
|
18082
|
+
const problem = versionFileProblem(o.repo.release.versionFiles[0], firstText);
|
|
18083
|
+
if (problem) {
|
|
18084
|
+
o.emit.error(problem);
|
|
18085
|
+
return null;
|
|
18086
|
+
}
|
|
18087
|
+
const current = JSON.parse(firstText).version;
|
|
18029
18088
|
if (!current) {
|
|
18030
18089
|
o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
|
|
18031
18090
|
return null;
|
|
@@ -18164,7 +18223,7 @@ function openReleaseCheckout(o) {
|
|
|
18164
18223
|
for (const f of o.repo.worktrees.copy) {
|
|
18165
18224
|
const src = join43(o.cwd, f);
|
|
18166
18225
|
if (!existsSync39(src)) continue;
|
|
18167
|
-
mkdirSync28(
|
|
18226
|
+
mkdirSync28(dirname24(join43(dir, f)), { recursive: true });
|
|
18168
18227
|
copyFileSync(src, join43(dir, f));
|
|
18169
18228
|
}
|
|
18170
18229
|
return { dir, branch, close };
|
|
@@ -18503,8 +18562,11 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
|
|
|
18503
18562
|
o.emit.emit(`${changed2.split("\n")[0]} changed in this release \u2014 continuing under the merged contract`);
|
|
18504
18563
|
}
|
|
18505
18564
|
} catch (e) {
|
|
18565
|
+
if (o.contractRefusal) throw new Error(`${o.contractRefusal}; the merged contract is also refused: ${e.message}`);
|
|
18506
18566
|
o.emit.warn(`could not re-read the merged repo config: ${e.message}; releasing under the earlier one`);
|
|
18507
18567
|
}
|
|
18568
|
+
} else if (o.contractRefusal) {
|
|
18569
|
+
throw new Error(`${o.contractRefusal}; nothing merged replaces it`);
|
|
18508
18570
|
}
|
|
18509
18571
|
}
|
|
18510
18572
|
if (o.mergeOnly) {
|
|
@@ -19504,7 +19566,7 @@ function nextRoles(decision, ran) {
|
|
|
19504
19566
|
|
|
19505
19567
|
// src/ship-name.ts
|
|
19506
19568
|
import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
|
|
19507
|
-
import { dirname as
|
|
19569
|
+
import { dirname as dirname25 } from "path";
|
|
19508
19570
|
import { parseDocument as parseDocument5 } from "yaml";
|
|
19509
19571
|
var ShipNameError = class extends Error {
|
|
19510
19572
|
};
|
|
@@ -19583,7 +19645,7 @@ function writeSyncedName(resolvedFile, name) {
|
|
|
19583
19645
|
}
|
|
19584
19646
|
if (raw.shipName === name) return;
|
|
19585
19647
|
raw.shipName = name;
|
|
19586
|
-
mkdirSync29(
|
|
19648
|
+
mkdirSync29(dirname25(resolvedFile), { recursive: true });
|
|
19587
19649
|
const tmp = `${resolvedFile}.tmp${process.pid}`;
|
|
19588
19650
|
writeFileSync32(tmp, `${JSON.stringify(raw, null, 2)}
|
|
19589
19651
|
`);
|
|
@@ -20277,7 +20339,7 @@ function describeCapability(i) {
|
|
|
20277
20339
|
|
|
20278
20340
|
// src/agents-sync-run.ts
|
|
20279
20341
|
import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
|
|
20280
|
-
import { dirname as
|
|
20342
|
+
import { dirname as dirname26 } from "path";
|
|
20281
20343
|
async function runAgentsSync(target, o) {
|
|
20282
20344
|
const dryRun = o.dryRun ?? false;
|
|
20283
20345
|
const force = o.force ?? false;
|
|
@@ -20314,7 +20376,7 @@ async function runAgentsSync(target, o) {
|
|
|
20314
20376
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
20315
20377
|
if (skillResult) raw.agentSkills = skillResult.agentSkills;
|
|
20316
20378
|
if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
|
|
20317
|
-
mkdirSync30(
|
|
20379
|
+
mkdirSync30(dirname26(resolvedPath), { recursive: true });
|
|
20318
20380
|
writeFileSync33(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
20319
20381
|
`);
|
|
20320
20382
|
if (target.resolved) {
|
|
@@ -20937,7 +20999,7 @@ ${run3.err}` });
|
|
|
20937
20999
|
}
|
|
20938
21000
|
|
|
20939
21001
|
// src/sample-ticket.ts
|
|
20940
|
-
import { basename as basename12, dirname as
|
|
21002
|
+
import { basename as basename12, dirname as dirname27, join as join44 } from "path";
|
|
20941
21003
|
var SAMPLE_REPO = "crew-sample";
|
|
20942
21004
|
var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
|
|
20943
21005
|
var SAMPLE_MARKER = "sample: true";
|
|
@@ -20960,7 +21022,7 @@ has something safe to work on: its test and build hooks always pass, it has no
|
|
|
20960
21022
|
remote, and \`crew sample-ticket --remove\` deletes it.
|
|
20961
21023
|
`;
|
|
20962
21024
|
function sampleDir(existing, reposBase, workspace) {
|
|
20963
|
-
if (existing.length > 0) return join44(
|
|
21025
|
+
if (existing.length > 0) return join44(dirname27(existing[0]), SAMPLE_REPO);
|
|
20964
21026
|
return join44(reposBase, workspace, SAMPLE_REPO);
|
|
20965
21027
|
}
|
|
20966
21028
|
var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
|
|
@@ -21006,7 +21068,7 @@ function planSampleTicket(i) {
|
|
|
21006
21068
|
|
|
21007
21069
|
// src/worktree-sweep.ts
|
|
21008
21070
|
import { existsSync as existsSync43, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
|
|
21009
|
-
import { basename as basename13, dirname as
|
|
21071
|
+
import { basename as basename13, dirname as dirname28, join as join45, resolve as resolve5 } from "path";
|
|
21010
21072
|
function planWorktreeSweep(target, tickets, contract) {
|
|
21011
21073
|
const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
|
|
21012
21074
|
const verified = contract.statuses.verified;
|
|
@@ -21070,7 +21132,7 @@ function worktreeTicketNumbers(target) {
|
|
|
21070
21132
|
return [...nums];
|
|
21071
21133
|
}
|
|
21072
21134
|
function siblingWorktreeDirs(target) {
|
|
21073
|
-
const parent =
|
|
21135
|
+
const parent = dirname28(target.dir.replace(/[/\\]+$/, ""));
|
|
21074
21136
|
const stem = `${basename13(target.dir.replace(/[/\\]+$/, ""))}-`;
|
|
21075
21137
|
const registered = new Set(worktrees(target.dir).map((w) => resolve5(w.path)));
|
|
21076
21138
|
let names;
|
|
@@ -21922,7 +21984,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
|
|
|
21922
21984
|
// src/ssh-keys.ts
|
|
21923
21985
|
import { execFileSync as execFileSync13 } from "child_process";
|
|
21924
21986
|
import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
|
|
21925
|
-
import { dirname as
|
|
21987
|
+
import { dirname as dirname29, join as join49 } from "path";
|
|
21926
21988
|
var SshKeyError = class extends Error {
|
|
21927
21989
|
};
|
|
21928
21990
|
function shipSshKeyPathFor(stateDir) {
|
|
@@ -21932,7 +21994,7 @@ function ensureShipSshKeypair(stateDir) {
|
|
|
21932
21994
|
const privateKeyPath = shipSshKeyPathFor(stateDir);
|
|
21933
21995
|
const publicKeyPath = `${privateKeyPath}.pub`;
|
|
21934
21996
|
if (!existsSync46(privateKeyPath)) {
|
|
21935
|
-
mkdirSync32(
|
|
21997
|
+
mkdirSync32(dirname29(privateKeyPath), { recursive: true });
|
|
21936
21998
|
try {
|
|
21937
21999
|
execFileSync13("ssh-keygen", [
|
|
21938
22000
|
"-t",
|
|
@@ -22124,7 +22186,7 @@ import { join as join50 } from "path";
|
|
|
22124
22186
|
|
|
22125
22187
|
// src/passenger-mirror.ts
|
|
22126
22188
|
import { mkdirSync as mkdirSync33 } from "fs";
|
|
22127
|
-
import { dirname as
|
|
22189
|
+
import { dirname as dirname30 } from "path";
|
|
22128
22190
|
function mirrorRepoOf(spec) {
|
|
22129
22191
|
const fromOrigin = spec.originUrl ? parseRemote(spec.originUrl) : null;
|
|
22130
22192
|
if (fromOrigin) return fromOrigin;
|
|
@@ -22142,7 +22204,7 @@ async function materializeMirrorCheckouts(specs, cacheFor = (s) => new SandboxCa
|
|
|
22142
22204
|
continue;
|
|
22143
22205
|
}
|
|
22144
22206
|
try {
|
|
22145
|
-
const parent =
|
|
22207
|
+
const parent = dirname30(spec.dir);
|
|
22146
22208
|
mkdirSync33(parent, { recursive: true });
|
|
22147
22209
|
mkdirSync33(spec.dir, { recursive: true });
|
|
22148
22210
|
const r = await cacheFor(spec).materialize(repo, spec.base, parent, spec.name);
|
|
@@ -22348,7 +22410,7 @@ import { createServer, request as httpRequest } from "http";
|
|
|
22348
22410
|
import { request as httpsRequest } from "https";
|
|
22349
22411
|
import { connect as netConnect } from "net";
|
|
22350
22412
|
import { connect as tlsConnect } from "tls";
|
|
22351
|
-
import { dirname as
|
|
22413
|
+
import { dirname as dirname31, isAbsolute as isAbsolute4 } from "path";
|
|
22352
22414
|
import { brotliDecompressSync, gunzipSync, inflateSync } from "zlib";
|
|
22353
22415
|
|
|
22354
22416
|
// src/periscope-inspector.ts
|
|
@@ -22462,7 +22524,7 @@ function parsePointedBody(raw) {
|
|
|
22462
22524
|
return entry;
|
|
22463
22525
|
}
|
|
22464
22526
|
function appendPointed(file, entry, now = /* @__PURE__ */ new Date()) {
|
|
22465
|
-
mkdirSync35(
|
|
22527
|
+
mkdirSync35(dirname31(file), { recursive: true });
|
|
22466
22528
|
appendFileSync3(file, `${JSON.stringify({ ...entry, at: now.toISOString() })}
|
|
22467
22529
|
`);
|
|
22468
22530
|
}
|
|
@@ -22781,9 +22843,32 @@ function describeCause(err3) {
|
|
|
22781
22843
|
return code && !first.includes(code) ? `${first} (${code})` : first;
|
|
22782
22844
|
}
|
|
22783
22845
|
|
|
22846
|
+
// src/contract-refusal.ts
|
|
22847
|
+
import { join as join51 } from "path";
|
|
22848
|
+
function contractFixers(dir, baseRef, tickets, locate) {
|
|
22849
|
+
const out = [];
|
|
22850
|
+
for (const t of tickets) {
|
|
22851
|
+
const branch = locate(t);
|
|
22852
|
+
if (!branch) continue;
|
|
22853
|
+
const changed2 = gitOk(dir, ["diff", "--name-only", `${baseRef}...${branch}`, "--", ...REPO_CONFIG_FILENAMES]);
|
|
22854
|
+
const file = changed2?.split("\n").find((f) => f.trim() !== "");
|
|
22855
|
+
if (!file) continue;
|
|
22856
|
+
const text2 = gitOk(dir, ["show", `${branch}:${file}`]);
|
|
22857
|
+
if (text2 === null) continue;
|
|
22858
|
+
try {
|
|
22859
|
+
parseRepoConfig(text2, join51(dir, file));
|
|
22860
|
+
} catch (e) {
|
|
22861
|
+
if (e instanceof RepoConfigError) continue;
|
|
22862
|
+
throw e;
|
|
22863
|
+
}
|
|
22864
|
+
out.push({ issueId: t.issue_id, branch, file });
|
|
22865
|
+
}
|
|
22866
|
+
return out;
|
|
22867
|
+
}
|
|
22868
|
+
|
|
22784
22869
|
// src/daemon.ts
|
|
22785
22870
|
import { existsSync as existsSync48, mkdirSync as mkdirSync36, readFileSync as readFileSync44, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
|
|
22786
|
-
import { join as
|
|
22871
|
+
import { join as join52 } from "path";
|
|
22787
22872
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
22788
22873
|
var DEFAULT_FLOOR_MS = 5e3;
|
|
22789
22874
|
var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
|
|
@@ -22871,7 +22956,7 @@ function latestMtime(path) {
|
|
|
22871
22956
|
if (!stat2.isDirectory()) return 0;
|
|
22872
22957
|
let latest = stat2.mtimeMs;
|
|
22873
22958
|
for (const entry of readdirSync17(path, { withFileTypes: true })) {
|
|
22874
|
-
latest = Math.max(latest, latestMtime(
|
|
22959
|
+
latest = Math.max(latest, latestMtime(join52(path, entry.name)));
|
|
22875
22960
|
}
|
|
22876
22961
|
return latest;
|
|
22877
22962
|
}
|
|
@@ -22883,7 +22968,7 @@ function makeStaleChecker(paths, opts = {}) {
|
|
|
22883
22968
|
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync48(opts.reloadSentinel);
|
|
22884
22969
|
}
|
|
22885
22970
|
function reloadSentinelFor(stateDir) {
|
|
22886
|
-
return
|
|
22971
|
+
return join52(stateDir, "reload-requested");
|
|
22887
22972
|
}
|
|
22888
22973
|
function readReloadRequestedAt(stateDir) {
|
|
22889
22974
|
try {
|
|
@@ -22893,7 +22978,7 @@ function readReloadRequestedAt(stateDir) {
|
|
|
22893
22978
|
}
|
|
22894
22979
|
}
|
|
22895
22980
|
function daemonStampFor(stateDir) {
|
|
22896
|
-
return
|
|
22981
|
+
return join52(stateDir, "daemon.started");
|
|
22897
22982
|
}
|
|
22898
22983
|
function noteDaemonStarted(stateDir, now = Date.now()) {
|
|
22899
22984
|
mkdirSync36(stateDir, { recursive: true });
|
|
@@ -23451,7 +23536,7 @@ async function buildEnvironment(route2, ship, ticket) {
|
|
|
23451
23536
|
name: t.name,
|
|
23452
23537
|
dir: t.dir,
|
|
23453
23538
|
id,
|
|
23454
|
-
config: resolveRepoConfig(
|
|
23539
|
+
config: resolveRepoConfig(loadRepoConfigTolerant(t.dir), {
|
|
23455
23540
|
hooks: { ...route2.hooks, ...o?.hooks },
|
|
23456
23541
|
labels: { ...route2.labels, ...o?.labels },
|
|
23457
23542
|
branch: { ...route2.branch, ...o?.branch },
|
|
@@ -23480,7 +23565,7 @@ import {
|
|
|
23480
23565
|
} from "fs";
|
|
23481
23566
|
import { createConnection } from "net";
|
|
23482
23567
|
import { createServer as createServer2 } from "http";
|
|
23483
|
-
import { basename as basename14, dirname as
|
|
23568
|
+
import { basename as basename14, dirname as dirname32, join as join53 } from "path";
|
|
23484
23569
|
import { execFile as execFile3, spawn as spawn12 } from "child_process";
|
|
23485
23570
|
|
|
23486
23571
|
// src/periscope-control.ts
|
|
@@ -23709,7 +23794,7 @@ function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
|
|
|
23709
23794
|
// src/control-api.ts
|
|
23710
23795
|
var PAUSABLE = ["dev", "design", "qa"];
|
|
23711
23796
|
var CONTROL_SOCKET_NAME = "control.sock";
|
|
23712
|
-
var controlSocketFor = (stateDir) =>
|
|
23797
|
+
var controlSocketFor = (stateDir) => join53(stateDir, CONTROL_SOCKET_NAME);
|
|
23713
23798
|
var MAX_SOCKET_PATH = 100;
|
|
23714
23799
|
var MAX_TAIL_BYTES = 1024 * 1024;
|
|
23715
23800
|
var MAX_WAIT_SECONDS = 30;
|
|
@@ -23796,7 +23881,7 @@ function candidates(live, rotated) {
|
|
|
23796
23881
|
const out = [live];
|
|
23797
23882
|
if (!rotated) return out;
|
|
23798
23883
|
try {
|
|
23799
|
-
for (const n of readdirSync18(
|
|
23884
|
+
for (const n of readdirSync18(dirname32(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join53(dirname32(live), n));
|
|
23800
23885
|
} catch {
|
|
23801
23886
|
}
|
|
23802
23887
|
return out;
|
|
@@ -23956,7 +24041,7 @@ async function tailReply(path, q2, rotated, abort) {
|
|
|
23956
24041
|
const deadline = Date.now() + Math.min(wait, MAX_WAIT_SECONDS) * 1e3;
|
|
23957
24042
|
let r = readTail(path, { after, tail: tail3, rotated });
|
|
23958
24043
|
while (after && !r.lines.length && !r.reset && Date.now() < deadline && !abort.aborted) {
|
|
23959
|
-
await waitForChange(
|
|
24044
|
+
await waitForChange(dirname32(path), deadline - Date.now(), abort);
|
|
23960
24045
|
r = readTail(path, { after: r.cursor, rotated });
|
|
23961
24046
|
}
|
|
23962
24047
|
return reply2(200, r);
|
|
@@ -23996,7 +24081,7 @@ async function route(deps, method, url, req, abort) {
|
|
|
23996
24081
|
if (p.startsWith("/streams/") && get2) {
|
|
23997
24082
|
const run3 = decodeURIComponent(p.slice("/streams/".length));
|
|
23998
24083
|
if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
|
|
23999
|
-
const file =
|
|
24084
|
+
const file = join53(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
|
|
24000
24085
|
if (!existsSync49(file)) return err2(404, "no such run");
|
|
24001
24086
|
return tailReply(file, url.searchParams, false, abort);
|
|
24002
24087
|
}
|
|
@@ -24146,7 +24231,7 @@ function crewJson(args, timeoutMs = 6e4) {
|
|
|
24146
24231
|
|
|
24147
24232
|
// src/periscope-mcp.ts
|
|
24148
24233
|
import { lstat, mkdir, readdir, readFile, readlink, realpath, rm, stat, writeFile } from "fs/promises";
|
|
24149
|
-
import { basename as basename15, dirname as
|
|
24234
|
+
import { basename as basename15, dirname as dirname33, isAbsolute as isAbsolute5, join as join55, relative as relative2, resolve as resolve6, sep } from "path";
|
|
24150
24235
|
import { z } from "zod";
|
|
24151
24236
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24152
24237
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -24154,11 +24239,11 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
24154
24239
|
// src/periscope-endpoints.ts
|
|
24155
24240
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
24156
24241
|
import { chmodSync as chmodSync5, existsSync as existsSync50, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync45, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
|
|
24157
|
-
import { join as
|
|
24158
|
-
var secretDir = (stateDir) =>
|
|
24159
|
-
var secretFile = (stateDir, id) =>
|
|
24160
|
-
var endpointDir = (stateDir) =>
|
|
24161
|
-
var endpointFile = (stateDir, id) =>
|
|
24242
|
+
import { join as join54 } from "path";
|
|
24243
|
+
var secretDir = (stateDir) => join54(stateDir, "periscope-mcp");
|
|
24244
|
+
var secretFile = (stateDir, id) => join54(secretDir(stateDir), `${id}.json`);
|
|
24245
|
+
var endpointDir = (stateDir) => join54(stateDir, "periscope-endpoints");
|
|
24246
|
+
var endpointFile = (stateDir, id) => join54(endpointDir(stateDir), `${id}.json`);
|
|
24162
24247
|
function mintPeriscopeSecret(stateDir, ticketRecordId) {
|
|
24163
24248
|
mkdirSync37(secretDir(stateDir), { recursive: true, mode: 448 });
|
|
24164
24249
|
const secret = randomBytes3(32).toString("base64url");
|
|
@@ -24284,10 +24369,10 @@ async function realAncestor(p) {
|
|
|
24284
24369
|
}
|
|
24285
24370
|
if (isLink) {
|
|
24286
24371
|
if (++hops > 40) throw new PathRefusal(`too many symbolic links resolving ${p}`);
|
|
24287
|
-
cur = resolve6(
|
|
24372
|
+
cur = resolve6(dirname33(cur), await readlink(cur));
|
|
24288
24373
|
continue;
|
|
24289
24374
|
}
|
|
24290
|
-
const parent =
|
|
24375
|
+
const parent = dirname33(cur);
|
|
24291
24376
|
if (parent === cur) throw new PathRefusal(`cannot resolve ${p}`);
|
|
24292
24377
|
tail3.push(basename15(cur));
|
|
24293
24378
|
cur = parent;
|
|
@@ -24299,13 +24384,13 @@ async function resolveInWorktree(root, requested) {
|
|
|
24299
24384
|
let abs;
|
|
24300
24385
|
if (isAbsolute5(requested)) {
|
|
24301
24386
|
abs = resolve6(requested);
|
|
24302
|
-
if (isInside(root, abs)) abs =
|
|
24387
|
+
if (isInside(root, abs)) abs = join55(realRoot, relative2(root, abs));
|
|
24303
24388
|
} else {
|
|
24304
24389
|
abs = resolve6(realRoot, requested);
|
|
24305
24390
|
}
|
|
24306
24391
|
if (!isInside(realRoot, abs)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
|
|
24307
24392
|
const { real, rest } = await realAncestor(abs);
|
|
24308
|
-
const final = rest ?
|
|
24393
|
+
const final = rest ? join55(real, rest) : real;
|
|
24309
24394
|
if (!isInside(realRoot, final)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
|
|
24310
24395
|
if (relative2(realRoot, final).split(sep).includes(".git")) throw new PathRefusal(".git is not reachable from a periscope");
|
|
24311
24396
|
return final;
|
|
@@ -24373,7 +24458,7 @@ function createPeriscopeMcpServer(d, sc) {
|
|
|
24373
24458
|
const entries = (await readdir(abs, { withFileTypes: true })).filter((e) => e.name !== ".git");
|
|
24374
24459
|
const lines2 = await Promise.all(entries.map(async (e) => {
|
|
24375
24460
|
if (e.isDirectory()) return `[DIR] ${e.name}`;
|
|
24376
|
-
const size = (await lstat(
|
|
24461
|
+
const size = (await lstat(join55(abs, e.name))).size;
|
|
24377
24462
|
return `[FILE] ${e.name} (${size} bytes)`;
|
|
24378
24463
|
}));
|
|
24379
24464
|
return text(lines2.join("\n") || "(empty)");
|
|
@@ -24398,7 +24483,7 @@ function createPeriscopeMcpServer(d, sc) {
|
|
|
24398
24483
|
if (Buffer.byteLength(content) > MAX_WRITE_BYTES) return text(`content exceeds ${MAX_WRITE_BYTES} bytes`, true);
|
|
24399
24484
|
const abs = await inTree(path);
|
|
24400
24485
|
if (abs === await realpath(root)) return text("path must name a file", true);
|
|
24401
|
-
await mkdir(
|
|
24486
|
+
await mkdir(dirname33(abs), { recursive: true });
|
|
24402
24487
|
await inTree(path);
|
|
24403
24488
|
await writeFile(abs, content);
|
|
24404
24489
|
return text(`Wrote ${Buffer.byteLength(content)} bytes to ${await rel(abs)}`);
|
|
@@ -24438,8 +24523,8 @@ function createPeriscopeMcpServer(d, sc) {
|
|
|
24438
24523
|
inputSchema: { path: z.string() },
|
|
24439
24524
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
24440
24525
|
}, async ({ path }) => {
|
|
24441
|
-
const parent = await inTree(
|
|
24442
|
-
const abs =
|
|
24526
|
+
const parent = await inTree(dirname33(path) === "" ? "." : dirname33(path));
|
|
24527
|
+
const abs = join55(parent, basename15(path));
|
|
24443
24528
|
if (basename15(path) === ".git" || abs === parent) return text("path must name a file", true);
|
|
24444
24529
|
const st = await lstat(abs).catch(() => void 0);
|
|
24445
24530
|
if (!st) return text(`${path} does not exist`, true);
|
|
@@ -24560,7 +24645,7 @@ function findPeriscope(sidecars, segment) {
|
|
|
24560
24645
|
}
|
|
24561
24646
|
|
|
24562
24647
|
// src/cli.ts
|
|
24563
|
-
var CREW_HOME = isCompiledBinary(import.meta.url) ?
|
|
24648
|
+
var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname34(process.execPath) : resolve7(dirname34(fileURLToPath2(import.meta.url)), "..");
|
|
24564
24649
|
var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
|
|
24565
24650
|
var CYCLE_SECONDS2 = 120;
|
|
24566
24651
|
var STALE_CYCLES = 3;
|
|
@@ -25278,7 +25363,19 @@ if (command === "passenger-sync-daemon") {
|
|
|
25278
25363
|
return;
|
|
25279
25364
|
}
|
|
25280
25365
|
try {
|
|
25281
|
-
|
|
25366
|
+
const refusedRepos = /* @__PURE__ */ new Map();
|
|
25367
|
+
const checkouts = syncAllPassengerCheckouts(cfg, (repo, e) => refusedRepos.set(repo, e.message));
|
|
25368
|
+
for (const [repo, detail] of refusedRepos) {
|
|
25369
|
+
if (await raiseShipAttention({ state, emit }, repoContractRefusedItem(repo, detail))) {
|
|
25370
|
+
emit.warn(`passenger sync skips ${repo}: ${detail}`, { step: "passengers", data: { repo } });
|
|
25371
|
+
}
|
|
25372
|
+
}
|
|
25373
|
+
for (const item of state.shipAttention().previous()) {
|
|
25374
|
+
if (item.kind === "repo_contract_refused" && !refusedRepos.has(item.key.slice(repoContractRefusedKey("").length))) {
|
|
25375
|
+
clearShipAttention(state, item.key);
|
|
25376
|
+
}
|
|
25377
|
+
}
|
|
25378
|
+
for (const r of checkouts) {
|
|
25282
25379
|
if (r.outcome.action === "clone-failed") {
|
|
25283
25380
|
if (!r.outcome.repeat) {
|
|
25284
25381
|
emit.warn(
|
|
@@ -25501,7 +25598,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25501
25598
|
}
|
|
25502
25599
|
if (host === "linux" && hasSystemd) {
|
|
25503
25600
|
const label = labelFor(CREW_HOME, "run");
|
|
25504
|
-
const staleTimer =
|
|
25601
|
+
const staleTimer = join56(homedir11(), ".config", "systemd", "user", `${label}.timer`);
|
|
25505
25602
|
if (existsSync51(staleTimer)) {
|
|
25506
25603
|
process.stdout.write(
|
|
25507
25604
|
`removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
|
|
@@ -25528,7 +25625,9 @@ if (command === "passenger-sync-daemon") {
|
|
|
25528
25625
|
}
|
|
25529
25626
|
const tracker = new Tracker(c, cfg.ship);
|
|
25530
25627
|
problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
|
|
25531
|
-
|
|
25628
|
+
const { settled: doctorRepos, errors: refusedRepos } = await settleRepos(reposOf(c), (t) => t.name, (t) => resolveOneRepo(c, t));
|
|
25629
|
+
for (const e of refusedRepos) problems.push(`repo ${e.repo}: ${e.detail}`);
|
|
25630
|
+
for (const r of doctorRepos) {
|
|
25532
25631
|
if (!existsSync51(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
|
|
25533
25632
|
if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
|
|
25534
25633
|
readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
|
|
@@ -25590,7 +25689,16 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25590
25689
|
`).join("")}` : "fix: nothing changed\n");
|
|
25591
25690
|
}
|
|
25592
25691
|
async function resolvedRepos(c) {
|
|
25593
|
-
|
|
25692
|
+
const { settled, errors } = await settleRepos(reposOf(c), (t) => t.name, (t) => resolveOneRepo(c, t));
|
|
25693
|
+
for (const e of errors) {
|
|
25694
|
+
try {
|
|
25695
|
+
await raiseShipAttention({ state, emit }, repoContractRefusedItem(e.repo, e.detail));
|
|
25696
|
+
} catch {
|
|
25697
|
+
}
|
|
25698
|
+
}
|
|
25699
|
+
const refused = new Set(errors.map((e) => e.repo));
|
|
25700
|
+
for (const t of reposOf(c)) if (!refused.has(t.name)) clearShipAttention(state, repoContractRefusedKey(t.name));
|
|
25701
|
+
return settled;
|
|
25594
25702
|
}
|
|
25595
25703
|
async function sandboxReposOf(routes) {
|
|
25596
25704
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -25709,7 +25817,10 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25709
25817
|
}
|
|
25710
25818
|
let boardLock;
|
|
25711
25819
|
try {
|
|
25712
|
-
|
|
25820
|
+
let contractRefusal = null;
|
|
25821
|
+
const repoFile = loadRepoConfigTolerant(target.dir, (e) => {
|
|
25822
|
+
contractRefusal = e;
|
|
25823
|
+
});
|
|
25713
25824
|
const o = c.repoOverrides[target.name];
|
|
25714
25825
|
const repo = resolveRepoConfig(repoFile, {
|
|
25715
25826
|
hooks: { ...c.hooks, ...o?.hooks },
|
|
@@ -25824,6 +25935,21 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25824
25935
|
releaseModes
|
|
25825
25936
|
);
|
|
25826
25937
|
reportUnplaceable(c.route, unplaceable, tracker.contract.statuses.verified, remit);
|
|
25938
|
+
if (contractRefusal) {
|
|
25939
|
+
const refusal = contractRefusal;
|
|
25940
|
+
const baseRef = remoteBranchExists(target.dir, repo.branch.remote, repo.branch.base) ? `${repo.branch.remote}/${repo.branch.base}` : repo.branch.base;
|
|
25941
|
+
const fixers = contractFixers(
|
|
25942
|
+
target.dir,
|
|
25943
|
+
baseRef,
|
|
25944
|
+
tickets.filter((t) => t.status === tracker.contract.statuses.verified),
|
|
25945
|
+
(t) => locateBranchForTicket(target.dir, repo, t)?.branch ?? null
|
|
25946
|
+
);
|
|
25947
|
+
if (fixers.length === 0) throw refusal;
|
|
25948
|
+
remit.emit(
|
|
25949
|
+
`${target.name}: the base contract is refused (${refusal.message}); ${fixers.map((f) => f.issueId).join(", ")} replace${fixers.length === 1 ? "s" : ""} ${fixers[0].file} \u2014 merging first`,
|
|
25950
|
+
{ step: "release" }
|
|
25951
|
+
);
|
|
25952
|
+
}
|
|
25827
25953
|
const blockerInfo = blockerInfoMap(all, await tracker.ticketsByIds(missingBlockerIds(all)));
|
|
25828
25954
|
const verifiedStatus = tracker.contract.statuses.verified;
|
|
25829
25955
|
const verifiedTickets = tickets.filter((t) => t.status === verifiedStatus);
|
|
@@ -25856,6 +25982,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25856
25982
|
emit: remit,
|
|
25857
25983
|
scope,
|
|
25858
25984
|
verifiedShas,
|
|
25985
|
+
...contractRefusal ? { contractRefusal: contractRefusal.message } : {},
|
|
25859
25986
|
state: state.release(scope),
|
|
25860
25987
|
reviewColumns: repo.release.mode === "external" ? await tracker.reviewColumns() : void 0,
|
|
25861
25988
|
dryRun,
|
|
@@ -26724,7 +26851,7 @@ ${cu.tail.trimEnd()}
|
|
|
26724
26851
|
process.exit(1);
|
|
26725
26852
|
}
|
|
26726
26853
|
requireArmed("run the daemon loop");
|
|
26727
|
-
const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [
|
|
26854
|
+
const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join56(CREW_HOME, "src"), join56(CREW_HOME, "dist"), join56(CREW_HOME, "bin")];
|
|
26728
26855
|
const configPaths = daemonConfigPaths(cfg);
|
|
26729
26856
|
watchPaths.push(...configPaths);
|
|
26730
26857
|
noteDaemonStarted(cfg.ship.stateDir);
|
|
@@ -27109,7 +27236,7 @@ ${cu.tail.trimEnd()}
|
|
|
27109
27236
|
const found = cfg.routes.find((r) => r.route === routeArg);
|
|
27110
27237
|
if (!found) throw new ReposError(`no route "${routeArg}" in the config (have: ${cfg.routes.map((r) => r.route).join(", ") || "none"})`);
|
|
27111
27238
|
const named2 = value("route") ? void 0 : positional[3];
|
|
27112
|
-
const dir = named2 ? repoDirOf(configText,
|
|
27239
|
+
const dir = named2 ? repoDirOf(configText, dirname34(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
|
|
27113
27240
|
const target = reposOf(found).find((t) => resolvePath(t.dir) === dir || t.name === named2);
|
|
27114
27241
|
const o = target ? found.repoOverrides[target.name] : void 0;
|
|
27115
27242
|
runReposCheck(dir, {
|
|
@@ -27133,7 +27260,7 @@ ${cu.tail.trimEnd()}
|
|
|
27133
27260
|
if (sub === "hooks") {
|
|
27134
27261
|
try {
|
|
27135
27262
|
const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
|
|
27136
|
-
const dir = repoDirOf(configText,
|
|
27263
|
+
const dir = repoDirOf(configText, dirname34(cfg.configFile), a.route, a.name);
|
|
27137
27264
|
if (!existsSync51(dir)) throw new ReposError(`${dir} does not exist on disk`);
|
|
27138
27265
|
const files = { exists: (r) => existsSync51(resolvePath(dir, r)), list: (r) => {
|
|
27139
27266
|
try {
|
|
@@ -27300,7 +27427,7 @@ ${cu.tail.trimEnd()}
|
|
|
27300
27427
|
}
|
|
27301
27428
|
const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
|
|
27302
27429
|
const text2 = configText;
|
|
27303
|
-
const base =
|
|
27430
|
+
const base = dirname34(cfg.configFile);
|
|
27304
27431
|
const isCheckout = (d) => existsSync51(`${d}/.git`);
|
|
27305
27432
|
try {
|
|
27306
27433
|
if (sub === "list") {
|
|
@@ -27315,7 +27442,8 @@ ${cu.tail.trimEnd()}
|
|
|
27315
27442
|
isCheckout,
|
|
27316
27443
|
repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
|
|
27317
27444
|
try {
|
|
27318
|
-
|
|
27445
|
+
const path = resolvePath(d, f);
|
|
27446
|
+
return Object.keys(parseRepoConfig(readFileSync46(path, "utf8"), path).hooks).length > 0;
|
|
27319
27447
|
} catch {
|
|
27320
27448
|
return false;
|
|
27321
27449
|
}
|
|
@@ -27978,7 +28106,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
27978
28106
|
process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
|
|
27979
28107
|
`);
|
|
27980
28108
|
} else {
|
|
27981
|
-
mkdirSync38(
|
|
28109
|
+
mkdirSync38(dirname34(resolvedPath), { recursive: true });
|
|
27982
28110
|
if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
|
|
27983
28111
|
const tmpPath = `${resolvedPath}.tmp${process.pid}`;
|
|
27984
28112
|
writeFileSync38(tmpPath, `${JSON.stringify(resolved, null, 2)}
|
|
@@ -28027,7 +28155,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28027
28155
|
process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
|
|
28028
28156
|
`);
|
|
28029
28157
|
} else {
|
|
28030
|
-
mkdirSync38(
|
|
28158
|
+
mkdirSync38(dirname34(apiKeyFile), { recursive: true });
|
|
28031
28159
|
writeFileSync38(apiKeyFile, `${apiKeyVar}=${apiKey}
|
|
28032
28160
|
`, { mode: 384 });
|
|
28033
28161
|
process.stderr.write(`API key written to ${apiKeyFile}
|
|
@@ -28724,10 +28852,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28724
28852
|
startupSeconds: cfg.ship.intelStartupSeconds,
|
|
28725
28853
|
prepare: async (path) => {
|
|
28726
28854
|
for (const f of repoWt.config.worktrees.copy) {
|
|
28727
|
-
const src =
|
|
28728
|
-
if (!existsSync51(src) || existsSync51(
|
|
28729
|
-
mkdirSync38(dirOf2(
|
|
28730
|
-
copyFileSync2(src,
|
|
28855
|
+
const src = join56(repoWt.dir, f);
|
|
28856
|
+
if (!existsSync51(src) || existsSync51(join56(path, f))) continue;
|
|
28857
|
+
mkdirSync38(dirOf2(join56(path, f)), { recursive: true });
|
|
28858
|
+
copyFileSync2(src, join56(path, f));
|
|
28731
28859
|
}
|
|
28732
28860
|
const setup = repoWt.config.hooks.setup;
|
|
28733
28861
|
return setup ? runScript(setup, { cwd: path, shell: shellWt }) : null;
|
|
@@ -29043,10 +29171,10 @@ ${where.join("\n")}`);
|
|
|
29043
29171
|
},
|
|
29044
29172
|
prepare: async (path) => {
|
|
29045
29173
|
for (const f of repoP.config.worktrees.copy) {
|
|
29046
|
-
const src =
|
|
29174
|
+
const src = join56(repoP.dir, f);
|
|
29047
29175
|
if (!existsSync51(src)) continue;
|
|
29048
|
-
mkdirSync38(dirOf2(
|
|
29049
|
-
copyFileSync2(src,
|
|
29176
|
+
mkdirSync38(dirOf2(join56(path, f)), { recursive: true });
|
|
29177
|
+
copyFileSync2(src, join56(path, f));
|
|
29050
29178
|
}
|
|
29051
29179
|
const setup = repoP.config.hooks.setup;
|
|
29052
29180
|
return setup ? runScript(setup, { cwd: path, shell: shellP, onLine: (l) => {
|
|
@@ -29500,7 +29628,7 @@ ${where.join("\n")}`);
|
|
|
29500
29628
|
if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
|
|
29501
29629
|
const tracker = new Tracker(route2, cfg.ship);
|
|
29502
29630
|
const configText = readFileSync46(cfg.configFile, "utf8");
|
|
29503
|
-
const cfgBase =
|
|
29631
|
+
const cfgBase = dirname34(cfg.configFile);
|
|
29504
29632
|
const configured2 = configuredRepos(configText, route2.route, cfgBase);
|
|
29505
29633
|
const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
|
|
29506
29634
|
const resolvedFile = resolvedPathFor(cfg.ship.stateDir, route2.route);
|
|
@@ -29544,12 +29672,12 @@ ${where.join("\n")}`);
|
|
|
29544
29672
|
`);
|
|
29545
29673
|
}
|
|
29546
29674
|
}
|
|
29547
|
-
if (existsSync51(
|
|
29675
|
+
if (existsSync51(join56(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
|
|
29548
29676
|
say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
|
|
29549
29677
|
break;
|
|
29550
29678
|
}
|
|
29551
29679
|
const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
|
|
29552
|
-
const fresh = !existsSync51(
|
|
29680
|
+
const fresh = !existsSync51(join56(dir, ".git"));
|
|
29553
29681
|
if (existsSync51(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
|
|
29554
29682
|
if (dryRun) {
|
|
29555
29683
|
say("dry-run", { dir, newCheckout: fresh, repoRow: row?.id ?? null }, `would ${fresh ? "create" : "reuse"} ${dir}${row ? "" : ", create the Repos row"}, and file one ${tracker.contract.statuses.approved} ticket`);
|
|
@@ -29557,8 +29685,8 @@ ${where.join("\n")}`);
|
|
|
29557
29685
|
}
|
|
29558
29686
|
if (fresh) {
|
|
29559
29687
|
mkdirSync38(dir, { recursive: true });
|
|
29560
|
-
writeFileSync38(
|
|
29561
|
-
writeFileSync38(
|
|
29688
|
+
writeFileSync38(join56(dir, "README.md"), SAMPLE_README);
|
|
29689
|
+
writeFileSync38(join56(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
|
|
29562
29690
|
const g = (...a) => {
|
|
29563
29691
|
const r = spawnSync11("git", a, { cwd: dir, encoding: "utf8" });
|
|
29564
29692
|
if (r.status !== 0) fail2(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
|
|
@@ -24682,6 +24682,19 @@ var SPEC_VERSION = 1;
|
|
|
24682
24682
|
var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
|
|
24683
24683
|
var RepoConfigError = class extends Error {
|
|
24684
24684
|
};
|
|
24685
|
+
function versionFileProblem(name, text) {
|
|
24686
|
+
const remedy = `a version file that is not a JSON object with a string "version" needs hooks.bump instead of release.versionFiles (docs/REPO_SPEC.md)`;
|
|
24687
|
+
let doc;
|
|
24688
|
+
try {
|
|
24689
|
+
doc = JSON.parse(text);
|
|
24690
|
+
} catch {
|
|
24691
|
+
return `release.versionFiles entry "${name}" is not a JSON document \u2014 ${remedy}`;
|
|
24692
|
+
}
|
|
24693
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc) || typeof doc.version !== "string") {
|
|
24694
|
+
return `release.versionFiles entry "${name}" has no string "version" field \u2014 ${remedy}`;
|
|
24695
|
+
}
|
|
24696
|
+
return null;
|
|
24697
|
+
}
|
|
24685
24698
|
var HOOK_NAMES = [
|
|
24686
24699
|
"test",
|
|
24687
24700
|
"build",
|
|
@@ -25015,6 +25028,22 @@ function parseRepoConfig(text, file) {
|
|
|
25015
25028
|
if (versionFiles.length === 0) {
|
|
25016
25029
|
throw new RepoConfigError(`${file}: release.versionFiles cannot be empty`);
|
|
25017
25030
|
}
|
|
25031
|
+
if (versionFilesGiven) {
|
|
25032
|
+
for (const name of versionFiles) {
|
|
25033
|
+
const path = (0, import_node_path6.join)((0, import_node_path6.dirname)(file), name);
|
|
25034
|
+
if (!(0, import_node_fs5.existsSync)(path)) continue;
|
|
25035
|
+
let text2;
|
|
25036
|
+
try {
|
|
25037
|
+
text2 = (0, import_node_fs5.readFileSync)(path, "utf8");
|
|
25038
|
+
} catch (e) {
|
|
25039
|
+
throw new RepoConfigError(
|
|
25040
|
+
`${file}: release.versionFiles entry "${name}" cannot be read (${e.code ?? "read error"})`
|
|
25041
|
+
);
|
|
25042
|
+
}
|
|
25043
|
+
const problem = versionFileProblem(name, text2);
|
|
25044
|
+
if (problem) throw new RepoConfigError(`${file}: ${problem}`);
|
|
25045
|
+
}
|
|
25046
|
+
}
|
|
25018
25047
|
const inspectorRaw = raw.periscope?.inspector;
|
|
25019
25048
|
if (inspectorRaw !== void 0 && inspectorRaw !== "proxy" && inspectorRaw !== "repo") {
|
|
25020
25049
|
throw new RepoConfigError(`${file}: periscope.inspector must be "proxy" or "repo"`);
|
package/package.json
CHANGED