terminalhire 0.42.22 → 0.42.24
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/bin/jpi-claim.js +1079 -300
- package/dist/bin/jpi-dispatch.js +1276 -498
- package/dist/bin/jpi-mcp.js +1102 -323
- package/dist/bin/jpi-run.js +928 -150
- package/dist/bin/jpi-statusline.js +1 -1
- package/dist/bin/proxy/egressProxy.js +89 -6
- package/dist/bin/proxy/proxyEntry.js +10 -2
- package/package.json +1 -1
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -1077,8 +1077,8 @@ function makeGitHubGovernor(fetchImpl, cfg) {
|
|
|
1077
1077
|
try {
|
|
1078
1078
|
const res = bound == null ? await fetchP : await Promise.race([
|
|
1079
1079
|
fetchP,
|
|
1080
|
-
new Promise((
|
|
1081
|
-
timer = setTimeout(() =>
|
|
1080
|
+
new Promise((resolve5) => {
|
|
1081
|
+
timer = setTimeout(() => resolve5(null), bound);
|
|
1082
1082
|
})
|
|
1083
1083
|
]);
|
|
1084
1084
|
if (!res || !res.ok) return null;
|
|
@@ -1893,11 +1893,11 @@ function parseGitHubRef(url) {
|
|
|
1893
1893
|
kind: m[3] === "pull" ? "pull" : "issue"
|
|
1894
1894
|
};
|
|
1895
1895
|
}
|
|
1896
|
-
async function ghGraphQL(
|
|
1896
|
+
async function ghGraphQL(query2, variables, token, signal, governor) {
|
|
1897
1897
|
const init = {
|
|
1898
1898
|
method: "POST",
|
|
1899
1899
|
headers: { ...ghHeaders(token), "Content-Type": "application/json" },
|
|
1900
|
-
body: JSON.stringify({ query, variables }),
|
|
1900
|
+
body: JSON.stringify({ query: query2, variables }),
|
|
1901
1901
|
signal
|
|
1902
1902
|
};
|
|
1903
1903
|
if (governor) {
|
|
@@ -5136,8 +5136,8 @@ async function getWithTimeout(governor, url, token, timeoutMs) {
|
|
|
5136
5136
|
try {
|
|
5137
5137
|
const res = await Promise.race([
|
|
5138
5138
|
getP,
|
|
5139
|
-
new Promise((
|
|
5140
|
-
timer = setTimeout(() =>
|
|
5139
|
+
new Promise((resolve5) => {
|
|
5140
|
+
timer = setTimeout(() => resolve5(null), timeoutMs);
|
|
5141
5141
|
})
|
|
5142
5142
|
]);
|
|
5143
5143
|
if (res === null) controller.abort();
|
|
@@ -6934,7 +6934,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
|
6934
6934
|
});
|
|
6935
6935
|
const { prehash } = eddsaOpts;
|
|
6936
6936
|
const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
|
|
6937
|
-
const
|
|
6937
|
+
const randomBytes13 = eddsaOpts.randomBytes || randomBytes;
|
|
6938
6938
|
const adjustScalarBytes2 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
|
|
6939
6939
|
const domain = eddsaOpts.domain || ((data, ctx, phflag) => {
|
|
6940
6940
|
_abool2(phflag, "phflag");
|
|
@@ -7016,7 +7016,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
|
7016
7016
|
signature: 2 * _size,
|
|
7017
7017
|
seed: _size
|
|
7018
7018
|
};
|
|
7019
|
-
function randomSecretKey(seed =
|
|
7019
|
+
function randomSecretKey(seed = randomBytes13(lengths.seed)) {
|
|
7020
7020
|
return _abytes2(seed, lengths.seed, "seed");
|
|
7021
7021
|
}
|
|
7022
7022
|
function keygen(seed) {
|
|
@@ -11342,7 +11342,7 @@ async function requireStoredLogin() {
|
|
|
11342
11342
|
}
|
|
11343
11343
|
}
|
|
11344
11344
|
function sleep(ms) {
|
|
11345
|
-
return new Promise((
|
|
11345
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
11346
11346
|
}
|
|
11347
11347
|
var TERMINALHIRE_DIR4, TOKEN_FILE, ALGO, IV_BYTES, GITHUB_SCOPE, DEVICE_CODE_URL, ACCESS_TOKEN_URL, BAKED_IN_CLIENT_ID, MOCK_TOKEN, MOCK_LOGIN;
|
|
11348
11348
|
var init_github_auth = __esm({
|
|
@@ -11759,16 +11759,16 @@ var sleep2;
|
|
|
11759
11759
|
var init_sleep = __esm({
|
|
11760
11760
|
"../../node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs"() {
|
|
11761
11761
|
"use strict";
|
|
11762
|
-
sleep2 = (ms, signal) => new Promise((
|
|
11762
|
+
sleep2 = (ms, signal) => new Promise((resolve5) => {
|
|
11763
11763
|
if (signal?.aborted)
|
|
11764
|
-
return
|
|
11764
|
+
return resolve5();
|
|
11765
11765
|
const onAbort = () => {
|
|
11766
11766
|
clearTimeout(timer);
|
|
11767
|
-
|
|
11767
|
+
resolve5();
|
|
11768
11768
|
};
|
|
11769
11769
|
const timer = setTimeout(() => {
|
|
11770
11770
|
signal?.removeEventListener("abort", onAbort);
|
|
11771
|
-
|
|
11771
|
+
resolve5();
|
|
11772
11772
|
}, ms);
|
|
11773
11773
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
11774
11774
|
});
|
|
@@ -12408,8 +12408,8 @@ var init_stringify = __esm({
|
|
|
12408
12408
|
});
|
|
12409
12409
|
|
|
12410
12410
|
// ../../node_modules/@anthropic-ai/sdk/internal/utils/query.mjs
|
|
12411
|
-
function stringifyQuery(
|
|
12412
|
-
return stringify(
|
|
12411
|
+
function stringifyQuery(query2) {
|
|
12412
|
+
return stringify(query2, { arrayFormat: "brackets" });
|
|
12413
12413
|
}
|
|
12414
12414
|
var init_query = __esm({
|
|
12415
12415
|
"../../node_modules/@anthropic-ai/sdk/internal/utils/query.mjs"() {
|
|
@@ -13897,8 +13897,8 @@ var init_api_promise = __esm({
|
|
|
13897
13897
|
init_parse();
|
|
13898
13898
|
APIPromise = class _APIPromise extends Promise {
|
|
13899
13899
|
constructor(client, responsePromise, parseResponse = defaultParseResponse) {
|
|
13900
|
-
super((
|
|
13901
|
-
|
|
13900
|
+
super((resolve5) => {
|
|
13901
|
+
resolve5(null);
|
|
13902
13902
|
});
|
|
13903
13903
|
this.responsePromise = responsePromise;
|
|
13904
13904
|
this.parseResponse = parseResponse;
|
|
@@ -14493,9 +14493,9 @@ var init_deployment_runs = __esm({
|
|
|
14493
14493
|
* ```
|
|
14494
14494
|
*/
|
|
14495
14495
|
list(params = {}, options) {
|
|
14496
|
-
const { betas, ...
|
|
14496
|
+
const { betas, ...query2 } = params ?? {};
|
|
14497
14497
|
return this._client.getAPIList("/v1/deployment_runs?beta=true", PageCursor, {
|
|
14498
|
-
query,
|
|
14498
|
+
query: query2,
|
|
14499
14499
|
...options,
|
|
14500
14500
|
headers: buildHeaders([
|
|
14501
14501
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -14607,9 +14607,9 @@ var init_deployments = __esm({
|
|
|
14607
14607
|
* ```
|
|
14608
14608
|
*/
|
|
14609
14609
|
list(params = {}, options) {
|
|
14610
|
-
const { betas, ...
|
|
14610
|
+
const { betas, ...query2 } = params ?? {};
|
|
14611
14611
|
return this._client.getAPIList("/v1/deployments?beta=true", PageCursor, {
|
|
14612
|
-
query,
|
|
14612
|
+
query: query2,
|
|
14613
14613
|
...options,
|
|
14614
14614
|
headers: buildHeaders([
|
|
14615
14615
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -14769,9 +14769,9 @@ var init_dreams = __esm({
|
|
|
14769
14769
|
* ```
|
|
14770
14770
|
*/
|
|
14771
14771
|
list(params = {}, options) {
|
|
14772
|
-
const { betas, ...
|
|
14772
|
+
const { betas, ...query2 } = params ?? {};
|
|
14773
14773
|
return this._client.getAPIList("/v1/dreams?beta=true", PageCursor, {
|
|
14774
|
-
query,
|
|
14774
|
+
query: query2,
|
|
14775
14775
|
...options,
|
|
14776
14776
|
headers: buildHeaders([
|
|
14777
14777
|
{ "anthropic-beta": [...betas ?? [], "dreaming-2026-04-21"].toString() },
|
|
@@ -14902,9 +14902,9 @@ var init_files = __esm({
|
|
|
14902
14902
|
* ```
|
|
14903
14903
|
*/
|
|
14904
14904
|
list(params = {}, options) {
|
|
14905
|
-
const { betas, ...
|
|
14905
|
+
const { betas, ...query2 } = params ?? {};
|
|
14906
14906
|
return this._client.getAPIList("/v1/files?beta=true", Page, {
|
|
14907
|
-
query,
|
|
14907
|
+
query: query2,
|
|
14908
14908
|
...options,
|
|
14909
14909
|
headers: buildHeaders([
|
|
14910
14910
|
{ "anthropic-beta": [...betas ?? [], "files-api-2025-04-14"].toString() },
|
|
@@ -15052,9 +15052,9 @@ var init_models = __esm({
|
|
|
15052
15052
|
* ```
|
|
15053
15053
|
*/
|
|
15054
15054
|
list(params = {}, options) {
|
|
15055
|
-
const { betas, ...
|
|
15055
|
+
const { betas, ...query2 } = params ?? {};
|
|
15056
15056
|
return this._client.getAPIList("/v1/models?beta=true", Page, {
|
|
15057
|
-
query,
|
|
15057
|
+
query: query2,
|
|
15058
15058
|
...options,
|
|
15059
15059
|
headers: buildHeaders([
|
|
15060
15060
|
{ ...betas?.toString() != null ? { "anthropic-beta": betas?.toString() } : void 0 },
|
|
@@ -15151,9 +15151,9 @@ var init_user_profiles = __esm({
|
|
|
15151
15151
|
* ```
|
|
15152
15152
|
*/
|
|
15153
15153
|
list(params = {}, options) {
|
|
15154
|
-
const { betas, ...
|
|
15154
|
+
const { betas, ...query2 } = params ?? {};
|
|
15155
15155
|
return this._client.getAPIList("/v1/user_profiles?beta=true", PageCursor, {
|
|
15156
|
-
query,
|
|
15156
|
+
query: query2,
|
|
15157
15157
|
...options,
|
|
15158
15158
|
headers: buildHeaders([
|
|
15159
15159
|
{ "anthropic-beta": [...betas ?? [], "user-profiles-2026-03-24"].toString() },
|
|
@@ -16012,9 +16012,9 @@ var init_versions = __esm({
|
|
|
16012
16012
|
* ```
|
|
16013
16013
|
*/
|
|
16014
16014
|
list(agentID, params = {}, options) {
|
|
16015
|
-
const { betas, ...
|
|
16015
|
+
const { betas, ...query2 } = params ?? {};
|
|
16016
16016
|
return this._client.getAPIList(path`/v1/agents/${agentID}/versions?beta=true`, PageCursor, {
|
|
16017
|
-
query,
|
|
16017
|
+
query: query2,
|
|
16018
16018
|
...options,
|
|
16019
16019
|
headers: buildHeaders([
|
|
16020
16020
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -16077,9 +16077,9 @@ var init_agents = __esm({
|
|
|
16077
16077
|
* ```
|
|
16078
16078
|
*/
|
|
16079
16079
|
retrieve(agentID, params = {}, options) {
|
|
16080
|
-
const { betas, ...
|
|
16080
|
+
const { betas, ...query2 } = params ?? {};
|
|
16081
16081
|
return this._client.get(path`/v1/agents/${agentID}?beta=true`, {
|
|
16082
|
-
query,
|
|
16082
|
+
query: query2,
|
|
16083
16083
|
...options,
|
|
16084
16084
|
headers: buildHeaders([
|
|
16085
16085
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -16122,9 +16122,9 @@ var init_agents = __esm({
|
|
|
16122
16122
|
* ```
|
|
16123
16123
|
*/
|
|
16124
16124
|
list(params = {}, options) {
|
|
16125
|
-
const { betas, ...
|
|
16125
|
+
const { betas, ...query2 } = params ?? {};
|
|
16126
16126
|
return this._client.getAPIList("/v1/agents?beta=true", PageCursor, {
|
|
16127
|
-
query,
|
|
16127
|
+
query: query2,
|
|
16128
16128
|
...options,
|
|
16129
16129
|
headers: buildHeaders([
|
|
16130
16130
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -16418,16 +16418,16 @@ var init_async_queue = __esm({
|
|
|
16418
16418
|
if (__classPrivateFieldGet(this, _AsyncQueue_closed, "f") || signal?.aborted) {
|
|
16419
16419
|
return Promise.resolve({ done: true, value: void 0 });
|
|
16420
16420
|
}
|
|
16421
|
-
return new Promise((
|
|
16421
|
+
return new Promise((resolve5) => {
|
|
16422
16422
|
const waiter = (r) => {
|
|
16423
16423
|
signal?.removeEventListener("abort", onAbort);
|
|
16424
|
-
|
|
16424
|
+
resolve5(r);
|
|
16425
16425
|
};
|
|
16426
16426
|
const onAbort = () => {
|
|
16427
16427
|
const idx = __classPrivateFieldGet(this, _AsyncQueue_waiters, "f").indexOf(waiter);
|
|
16428
16428
|
if (idx >= 0)
|
|
16429
16429
|
__classPrivateFieldGet(this, _AsyncQueue_waiters, "f").splice(idx, 1);
|
|
16430
|
-
|
|
16430
|
+
resolve5({ done: true, value: void 0 });
|
|
16431
16431
|
};
|
|
16432
16432
|
__classPrivateFieldGet(this, _AsyncQueue_waiters, "f").push(waiter);
|
|
16433
16433
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -17027,13 +17027,13 @@ var init_json_schema = __esm({
|
|
|
17027
17027
|
|
|
17028
17028
|
// ../../node_modules/@anthropic-ai/sdk/internal/utils/promise.mjs
|
|
17029
17029
|
function promiseWithResolvers() {
|
|
17030
|
-
let
|
|
17030
|
+
let resolve5;
|
|
17031
17031
|
let reject;
|
|
17032
17032
|
const promise = new Promise((res, rej) => {
|
|
17033
|
-
|
|
17033
|
+
resolve5 = res;
|
|
17034
17034
|
reject = rej;
|
|
17035
17035
|
});
|
|
17036
|
-
return { promise, resolve:
|
|
17036
|
+
return { promise, resolve: resolve5, reject };
|
|
17037
17037
|
}
|
|
17038
17038
|
var init_promise = __esm({
|
|
17039
17039
|
"../../node_modules/@anthropic-ai/sdk/internal/utils/promise.mjs"() {
|
|
@@ -17635,7 +17635,7 @@ function betaGrepTool(ctx) {
|
|
|
17635
17635
|
});
|
|
17636
17636
|
}
|
|
17637
17637
|
function runRipgrep(rg, pattern, searchPath, signal) {
|
|
17638
|
-
return new Promise((
|
|
17638
|
+
return new Promise((resolve5, reject) => {
|
|
17639
17639
|
const proc = cp.spawn(rg, ["-n", "--no-heading", "-e", pattern, "--", searchPath], {
|
|
17640
17640
|
...signal ? { signal } : {}
|
|
17641
17641
|
});
|
|
@@ -17657,12 +17657,12 @@ function runRipgrep(rg, pattern, searchPath, signal) {
|
|
|
17657
17657
|
if (signal?.aborted)
|
|
17658
17658
|
return reject(new ToolError("grep: aborted"));
|
|
17659
17659
|
if (truncated)
|
|
17660
|
-
return
|
|
17660
|
+
return resolve5(out + `
|
|
17661
17661
|
[output truncated at ${GREP_OUTPUT_LIMIT} bytes]`);
|
|
17662
17662
|
if (code === 0)
|
|
17663
|
-
return
|
|
17663
|
+
return resolve5(out);
|
|
17664
17664
|
if (code === 1)
|
|
17665
|
-
return
|
|
17665
|
+
return resolve5("no matches");
|
|
17666
17666
|
reject(new ToolError(`grep: rg failed: ${errOut || `exit ${code}`}`));
|
|
17667
17667
|
});
|
|
17668
17668
|
proc.on("error", (e) => {
|
|
@@ -17840,8 +17840,8 @@ var init_node = __esm({
|
|
|
17840
17840
|
`;
|
|
17841
17841
|
__classPrivateFieldGet(this, _BashSession_proc, "f").stdin.write(wrapped);
|
|
17842
17842
|
if (__classPrivateFieldGet(this, _BashSession_buf, "f").indexOf(sentinel2) < 0) {
|
|
17843
|
-
const { promise: sentinelSeen, resolve:
|
|
17844
|
-
__classPrivateFieldSet(this, _BashSession_waiting, { sentinel: sentinel2, resolve:
|
|
17843
|
+
const { promise: sentinelSeen, resolve: resolve5 } = promiseWithResolvers();
|
|
17844
|
+
__classPrivateFieldSet(this, _BashSession_waiting, { sentinel: sentinel2, resolve: resolve5 }, "f");
|
|
17845
17845
|
let timer;
|
|
17846
17846
|
let onAbort;
|
|
17847
17847
|
try {
|
|
@@ -18214,9 +18214,9 @@ var init_work = __esm({
|
|
|
18214
18214
|
* ```
|
|
18215
18215
|
*/
|
|
18216
18216
|
list(environmentID, params = {}, options) {
|
|
18217
|
-
const { betas, ...
|
|
18217
|
+
const { betas, ...query2 } = params ?? {};
|
|
18218
18218
|
return this._client.getAPIList(path`/v1/environments/${environmentID}/work?beta=true`, PageCursor, {
|
|
18219
|
-
query,
|
|
18219
|
+
query: query2,
|
|
18220
18220
|
...options,
|
|
18221
18221
|
headers: buildHeaders([
|
|
18222
18222
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -18295,9 +18295,9 @@ var init_work = __esm({
|
|
|
18295
18295
|
* ```
|
|
18296
18296
|
*/
|
|
18297
18297
|
poll(environmentID, params = {}, options) {
|
|
18298
|
-
const { betas, "Anthropic-Worker-ID": anthropicWorkerID, ...
|
|
18298
|
+
const { betas, "Anthropic-Worker-ID": anthropicWorkerID, ...query2 } = params ?? {};
|
|
18299
18299
|
return this._client.get(path`/v1/environments/${environmentID}/work/poll?beta=true`, {
|
|
18300
|
-
query,
|
|
18300
|
+
query: query2,
|
|
18301
18301
|
...options,
|
|
18302
18302
|
headers: buildHeaders([
|
|
18303
18303
|
{
|
|
@@ -18494,9 +18494,9 @@ var init_environments = __esm({
|
|
|
18494
18494
|
* ```
|
|
18495
18495
|
*/
|
|
18496
18496
|
list(params = {}, options) {
|
|
18497
|
-
const { betas, ...
|
|
18497
|
+
const { betas, ...query2 } = params ?? {};
|
|
18498
18498
|
return this._client.getAPIList("/v1/environments?beta=true", PageCursor, {
|
|
18499
|
-
query,
|
|
18499
|
+
query: query2,
|
|
18500
18500
|
...options,
|
|
18501
18501
|
headers: buildHeaders([
|
|
18502
18502
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -18599,9 +18599,9 @@ var init_memories = __esm({
|
|
|
18599
18599
|
* ```
|
|
18600
18600
|
*/
|
|
18601
18601
|
retrieve(memoryID, params, options) {
|
|
18602
|
-
const { memory_store_id, betas, ...
|
|
18602
|
+
const { memory_store_id, betas, ...query2 } = params;
|
|
18603
18603
|
return this._client.get(path`/v1/memory_stores/${memory_store_id}/memories/${memoryID}?beta=true`, {
|
|
18604
|
-
query,
|
|
18604
|
+
query: query2,
|
|
18605
18605
|
...options,
|
|
18606
18606
|
headers: buildHeaders([
|
|
18607
18607
|
{ "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
|
|
@@ -18647,9 +18647,9 @@ var init_memories = __esm({
|
|
|
18647
18647
|
* ```
|
|
18648
18648
|
*/
|
|
18649
18649
|
list(memoryStoreID, params = {}, options) {
|
|
18650
|
-
const { betas, ...
|
|
18650
|
+
const { betas, ...query2 } = params ?? {};
|
|
18651
18651
|
return this._client.getAPIList(path`/v1/memory_stores/${memoryStoreID}/memories?beta=true`, PageCursor, {
|
|
18652
|
-
query,
|
|
18652
|
+
query: query2,
|
|
18653
18653
|
...options,
|
|
18654
18654
|
headers: buildHeaders([
|
|
18655
18655
|
{ "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
|
|
@@ -18707,9 +18707,9 @@ var init_memory_versions = __esm({
|
|
|
18707
18707
|
* ```
|
|
18708
18708
|
*/
|
|
18709
18709
|
retrieve(memoryVersionID, params, options) {
|
|
18710
|
-
const { memory_store_id, betas, ...
|
|
18710
|
+
const { memory_store_id, betas, ...query2 } = params;
|
|
18711
18711
|
return this._client.get(path`/v1/memory_stores/${memory_store_id}/memory_versions/${memoryVersionID}?beta=true`, {
|
|
18712
|
-
query,
|
|
18712
|
+
query: query2,
|
|
18713
18713
|
...options,
|
|
18714
18714
|
headers: buildHeaders([
|
|
18715
18715
|
{ "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
|
|
@@ -18731,9 +18731,9 @@ var init_memory_versions = __esm({
|
|
|
18731
18731
|
* ```
|
|
18732
18732
|
*/
|
|
18733
18733
|
list(memoryStoreID, params = {}, options) {
|
|
18734
|
-
const { betas, ...
|
|
18734
|
+
const { betas, ...query2 } = params ?? {};
|
|
18735
18735
|
return this._client.getAPIList(path`/v1/memory_stores/${memoryStoreID}/memory_versions?beta=true`, PageCursor, {
|
|
18736
|
-
query,
|
|
18736
|
+
query: query2,
|
|
18737
18737
|
...options,
|
|
18738
18738
|
headers: buildHeaders([
|
|
18739
18739
|
{ "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
|
|
@@ -18859,9 +18859,9 @@ var init_memory_stores = __esm({
|
|
|
18859
18859
|
* ```
|
|
18860
18860
|
*/
|
|
18861
18861
|
list(params = {}, options) {
|
|
18862
|
-
const { betas, ...
|
|
18862
|
+
const { betas, ...query2 } = params ?? {};
|
|
18863
18863
|
return this._client.getAPIList("/v1/memory_stores?beta=true", PageCursor, {
|
|
18864
|
-
query,
|
|
18864
|
+
query: query2,
|
|
18865
18865
|
...options,
|
|
18866
18866
|
headers: buildHeaders([
|
|
18867
18867
|
{ "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
|
|
@@ -19059,9 +19059,9 @@ var init_batches = __esm({
|
|
|
19059
19059
|
* ```
|
|
19060
19060
|
*/
|
|
19061
19061
|
list(params = {}, options) {
|
|
19062
|
-
const { betas, ...
|
|
19062
|
+
const { betas, ...query2 } = params ?? {};
|
|
19063
19063
|
return this._client.getAPIList("/v1/messages/batches?beta=true", Page, {
|
|
19064
|
-
query,
|
|
19064
|
+
query: query2,
|
|
19065
19065
|
...options,
|
|
19066
19066
|
headers: buildHeaders([
|
|
19067
19067
|
{ "anthropic-beta": [...betas ?? [], "message-batches-2024-09-24"].toString() },
|
|
@@ -19596,12 +19596,12 @@ var init_BetaMessageStream = __esm({
|
|
|
19596
19596
|
}
|
|
19597
19597
|
return this._emit("error", new AnthropicError(String(error)));
|
|
19598
19598
|
});
|
|
19599
|
-
__classPrivateFieldSet(this, _BetaMessageStream_connectedPromise, new Promise((
|
|
19600
|
-
__classPrivateFieldSet(this, _BetaMessageStream_resolveConnectedPromise,
|
|
19599
|
+
__classPrivateFieldSet(this, _BetaMessageStream_connectedPromise, new Promise((resolve5, reject) => {
|
|
19600
|
+
__classPrivateFieldSet(this, _BetaMessageStream_resolveConnectedPromise, resolve5, "f");
|
|
19601
19601
|
__classPrivateFieldSet(this, _BetaMessageStream_rejectConnectedPromise, reject, "f");
|
|
19602
19602
|
}), "f");
|
|
19603
|
-
__classPrivateFieldSet(this, _BetaMessageStream_endPromise, new Promise((
|
|
19604
|
-
__classPrivateFieldSet(this, _BetaMessageStream_resolveEndPromise,
|
|
19603
|
+
__classPrivateFieldSet(this, _BetaMessageStream_endPromise, new Promise((resolve5, reject) => {
|
|
19604
|
+
__classPrivateFieldSet(this, _BetaMessageStream_resolveEndPromise, resolve5, "f");
|
|
19605
19605
|
__classPrivateFieldSet(this, _BetaMessageStream_rejectEndPromise, reject, "f");
|
|
19606
19606
|
}), "f");
|
|
19607
19607
|
__classPrivateFieldGet(this, _BetaMessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -19771,11 +19771,11 @@ var init_BetaMessageStream = __esm({
|
|
|
19771
19771
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
19772
19772
|
*/
|
|
19773
19773
|
emitted(event) {
|
|
19774
|
-
return new Promise((
|
|
19774
|
+
return new Promise((resolve5, reject) => {
|
|
19775
19775
|
__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
|
|
19776
19776
|
if (event !== "error")
|
|
19777
19777
|
this.once("error", reject);
|
|
19778
|
-
this.once(event,
|
|
19778
|
+
this.once(event, resolve5);
|
|
19779
19779
|
});
|
|
19780
19780
|
}
|
|
19781
19781
|
async done() {
|
|
@@ -20137,7 +20137,7 @@ var init_BetaMessageStream = __esm({
|
|
|
20137
20137
|
if (done) {
|
|
20138
20138
|
return { value: void 0, done: true };
|
|
20139
20139
|
}
|
|
20140
|
-
return new Promise((
|
|
20140
|
+
return new Promise((resolve5, reject) => readQueue.push({ resolve: resolve5, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
20141
20141
|
}
|
|
20142
20142
|
const chunk = pushQueue.shift();
|
|
20143
20143
|
return { value: chunk, done: false };
|
|
@@ -20734,9 +20734,9 @@ var init_events = __esm({
|
|
|
20734
20734
|
* ```
|
|
20735
20735
|
*/
|
|
20736
20736
|
list(sessionID, params = {}, options) {
|
|
20737
|
-
const { betas, ...
|
|
20737
|
+
const { betas, ...query2 } = params ?? {};
|
|
20738
20738
|
return this._client.getAPIList(path`/v1/sessions/${sessionID}/events?beta=true`, PageCursor, {
|
|
20739
|
-
query,
|
|
20739
|
+
query: query2,
|
|
20740
20740
|
...options,
|
|
20741
20741
|
headers: buildHeaders([
|
|
20742
20742
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -20791,9 +20791,9 @@ var init_events = __esm({
|
|
|
20791
20791
|
* ```
|
|
20792
20792
|
*/
|
|
20793
20793
|
stream(sessionID, params = {}, options) {
|
|
20794
|
-
const { betas, ...
|
|
20794
|
+
const { betas, ...query2 } = params ?? {};
|
|
20795
20795
|
return this._client.get(path`/v1/sessions/${sessionID}/events/stream?beta=true`, {
|
|
20796
|
-
query,
|
|
20796
|
+
query: query2,
|
|
20797
20797
|
...options,
|
|
20798
20798
|
headers: buildHeaders([
|
|
20799
20799
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -20901,9 +20901,9 @@ var init_resources = __esm({
|
|
|
20901
20901
|
* ```
|
|
20902
20902
|
*/
|
|
20903
20903
|
list(sessionID, params = {}, options) {
|
|
20904
|
-
const { betas, ...
|
|
20904
|
+
const { betas, ...query2 } = params ?? {};
|
|
20905
20905
|
return this._client.getAPIList(path`/v1/sessions/${sessionID}/resources?beta=true`, PageCursor, {
|
|
20906
|
-
query,
|
|
20906
|
+
query: query2,
|
|
20907
20907
|
...options,
|
|
20908
20908
|
headers: buildHeaders([
|
|
20909
20909
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -20988,9 +20988,9 @@ var init_events2 = __esm({
|
|
|
20988
20988
|
* ```
|
|
20989
20989
|
*/
|
|
20990
20990
|
list(threadID, params, options) {
|
|
20991
|
-
const { session_id, betas, ...
|
|
20991
|
+
const { session_id, betas, ...query2 } = params;
|
|
20992
20992
|
return this._client.getAPIList(path`/v1/sessions/${session_id}/threads/${threadID}/events?beta=true`, PageCursor, {
|
|
20993
|
-
query,
|
|
20993
|
+
query: query2,
|
|
20994
20994
|
...options,
|
|
20995
20995
|
headers: buildHeaders([
|
|
20996
20996
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -21077,9 +21077,9 @@ var init_threads = __esm({
|
|
|
21077
21077
|
* ```
|
|
21078
21078
|
*/
|
|
21079
21079
|
list(sessionID, params = {}, options) {
|
|
21080
|
-
const { betas, ...
|
|
21080
|
+
const { betas, ...query2 } = params ?? {};
|
|
21081
21081
|
return this._client.getAPIList(path`/v1/sessions/${sessionID}/threads?beta=true`, PageCursor, {
|
|
21082
|
-
query,
|
|
21082
|
+
query: query2,
|
|
21083
21083
|
...options,
|
|
21084
21084
|
headers: buildHeaders([
|
|
21085
21085
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -21214,9 +21214,9 @@ var init_sessions = __esm({
|
|
|
21214
21214
|
* ```
|
|
21215
21215
|
*/
|
|
21216
21216
|
list(params = {}, options) {
|
|
21217
|
-
const { betas, ...
|
|
21217
|
+
const { betas, ...query2 } = params ?? {};
|
|
21218
21218
|
return this._client.getAPIList("/v1/sessions?beta=true", BidirectionalPageCursor, {
|
|
21219
|
-
query,
|
|
21219
|
+
query: query2,
|
|
21220
21220
|
...options,
|
|
21221
21221
|
headers: buildHeaders([
|
|
21222
21222
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -21341,9 +21341,9 @@ var init_versions2 = __esm({
|
|
|
21341
21341
|
* ```
|
|
21342
21342
|
*/
|
|
21343
21343
|
list(skillID, params = {}, options) {
|
|
21344
|
-
const { betas, ...
|
|
21344
|
+
const { betas, ...query2 } = params ?? {};
|
|
21345
21345
|
return this._client.getAPIList(path`/v1/skills/${skillID}/versions?beta=true`, PageCursor, {
|
|
21346
|
-
query,
|
|
21346
|
+
query: query2,
|
|
21347
21347
|
...options,
|
|
21348
21348
|
headers: buildHeaders([
|
|
21349
21349
|
{ "anthropic-beta": [...betas ?? [], "skills-2025-10-02"].toString() },
|
|
@@ -21472,9 +21472,9 @@ var init_skills2 = __esm({
|
|
|
21472
21472
|
* ```
|
|
21473
21473
|
*/
|
|
21474
21474
|
list(params = {}, options) {
|
|
21475
|
-
const { betas, ...
|
|
21475
|
+
const { betas, ...query2 } = params ?? {};
|
|
21476
21476
|
return this._client.getAPIList("/v1/skills?beta=true", PageCursor, {
|
|
21477
|
-
query,
|
|
21477
|
+
query: query2,
|
|
21478
21478
|
...options,
|
|
21479
21479
|
headers: buildHeaders([
|
|
21480
21480
|
{ "anthropic-beta": [...betas ?? [], "skills-2025-10-02"].toString() },
|
|
@@ -21592,9 +21592,9 @@ var init_certificates = __esm({
|
|
|
21592
21592
|
* ```
|
|
21593
21593
|
*/
|
|
21594
21594
|
list(tunnelID, params = {}, options) {
|
|
21595
|
-
const { betas, ...
|
|
21595
|
+
const { betas, ...query2 } = params ?? {};
|
|
21596
21596
|
return this._client.getAPIList(path`/v1/tunnels/${tunnelID}/certificates?beta=true`, PageCursor, {
|
|
21597
|
-
query,
|
|
21597
|
+
query: query2,
|
|
21598
21598
|
...options,
|
|
21599
21599
|
headers: buildHeaders([
|
|
21600
21600
|
{ "anthropic-beta": [...betas ?? [], "mcp-tunnels-2026-06-22"].toString() },
|
|
@@ -21721,9 +21721,9 @@ var init_tunnels = __esm({
|
|
|
21721
21721
|
* ```
|
|
21722
21722
|
*/
|
|
21723
21723
|
list(params = {}, options) {
|
|
21724
|
-
const { betas, ...
|
|
21724
|
+
const { betas, ...query2 } = params ?? {};
|
|
21725
21725
|
return this._client.getAPIList("/v1/tunnels?beta=true", PageCursor, {
|
|
21726
|
-
query,
|
|
21726
|
+
query: query2,
|
|
21727
21727
|
...options,
|
|
21728
21728
|
headers: buildHeaders([
|
|
21729
21729
|
{ "anthropic-beta": [...betas ?? [], "mcp-tunnels-2026-06-22"].toString() },
|
|
@@ -21917,9 +21917,9 @@ var init_credentials2 = __esm({
|
|
|
21917
21917
|
* ```
|
|
21918
21918
|
*/
|
|
21919
21919
|
list(vaultID, params = {}, options) {
|
|
21920
|
-
const { betas, ...
|
|
21920
|
+
const { betas, ...query2 } = params ?? {};
|
|
21921
21921
|
return this._client.getAPIList(path`/v1/vaults/${vaultID}/credentials?beta=true`, PageCursor, {
|
|
21922
|
-
query,
|
|
21922
|
+
query: query2,
|
|
21923
21923
|
...options,
|
|
21924
21924
|
headers: buildHeaders([
|
|
21925
21925
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -22090,9 +22090,9 @@ var init_vaults = __esm({
|
|
|
22090
22090
|
* ```
|
|
22091
22091
|
*/
|
|
22092
22092
|
list(params = {}, options) {
|
|
22093
|
-
const { betas, ...
|
|
22093
|
+
const { betas, ...query2 } = params ?? {};
|
|
22094
22094
|
return this._client.getAPIList("/v1/vaults?beta=true", PageCursor, {
|
|
22095
|
-
query,
|
|
22095
|
+
query: query2,
|
|
22096
22096
|
...options,
|
|
22097
22097
|
headers: buildHeaders([
|
|
22098
22098
|
{ "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
|
|
@@ -22375,12 +22375,12 @@ var init_MessageStream = __esm({
|
|
|
22375
22375
|
}
|
|
22376
22376
|
return this._emit("error", new AnthropicError(String(error)));
|
|
22377
22377
|
});
|
|
22378
|
-
__classPrivateFieldSet(this, _MessageStream_connectedPromise, new Promise((
|
|
22379
|
-
__classPrivateFieldSet(this, _MessageStream_resolveConnectedPromise,
|
|
22378
|
+
__classPrivateFieldSet(this, _MessageStream_connectedPromise, new Promise((resolve5, reject) => {
|
|
22379
|
+
__classPrivateFieldSet(this, _MessageStream_resolveConnectedPromise, resolve5, "f");
|
|
22380
22380
|
__classPrivateFieldSet(this, _MessageStream_rejectConnectedPromise, reject, "f");
|
|
22381
22381
|
}), "f");
|
|
22382
|
-
__classPrivateFieldSet(this, _MessageStream_endPromise, new Promise((
|
|
22383
|
-
__classPrivateFieldSet(this, _MessageStream_resolveEndPromise,
|
|
22382
|
+
__classPrivateFieldSet(this, _MessageStream_endPromise, new Promise((resolve5, reject) => {
|
|
22383
|
+
__classPrivateFieldSet(this, _MessageStream_resolveEndPromise, resolve5, "f");
|
|
22384
22384
|
__classPrivateFieldSet(this, _MessageStream_rejectEndPromise, reject, "f");
|
|
22385
22385
|
}), "f");
|
|
22386
22386
|
__classPrivateFieldGet(this, _MessageStream_connectedPromise, "f").catch(() => {
|
|
@@ -22550,11 +22550,11 @@ var init_MessageStream = __esm({
|
|
|
22550
22550
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
22551
22551
|
*/
|
|
22552
22552
|
emitted(event) {
|
|
22553
|
-
return new Promise((
|
|
22553
|
+
return new Promise((resolve5, reject) => {
|
|
22554
22554
|
__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
|
|
22555
22555
|
if (event !== "error")
|
|
22556
22556
|
this.once("error", reject);
|
|
22557
|
-
this.once(event,
|
|
22557
|
+
this.once(event, resolve5);
|
|
22558
22558
|
});
|
|
22559
22559
|
}
|
|
22560
22560
|
async done() {
|
|
@@ -22875,7 +22875,7 @@ var init_MessageStream = __esm({
|
|
|
22875
22875
|
if (done) {
|
|
22876
22876
|
return { value: void 0, done: true };
|
|
22877
22877
|
}
|
|
22878
|
-
return new Promise((
|
|
22878
|
+
return new Promise((resolve5, reject) => readQueue.push({ resolve: resolve5, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
22879
22879
|
}
|
|
22880
22880
|
const chunk = pushQueue.shift();
|
|
22881
22881
|
return { value: chunk, done: false };
|
|
@@ -22978,8 +22978,8 @@ var init_batches2 = __esm({
|
|
|
22978
22978
|
* }
|
|
22979
22979
|
* ```
|
|
22980
22980
|
*/
|
|
22981
|
-
list(
|
|
22982
|
-
return this._client.getAPIList("/v1/messages/batches", Page, { query, ...options });
|
|
22981
|
+
list(query2 = {}, options) {
|
|
22982
|
+
return this._client.getAPIList("/v1/messages/batches", Page, { query: query2, ...options });
|
|
22983
22983
|
}
|
|
22984
22984
|
/**
|
|
22985
22985
|
* Delete a Message Batch.
|
|
@@ -23235,9 +23235,9 @@ var init_models2 = __esm({
|
|
|
23235
23235
|
* use in the API. More recently released models are listed first.
|
|
23236
23236
|
*/
|
|
23237
23237
|
list(params = {}, options) {
|
|
23238
|
-
const { betas, ...
|
|
23238
|
+
const { betas, ...query2 } = params ?? {};
|
|
23239
23239
|
return this._client.getAPIList("/v1/models", Page, {
|
|
23240
|
-
query,
|
|
23240
|
+
query: query2,
|
|
23241
23241
|
...options,
|
|
23242
23242
|
headers: buildHeaders([
|
|
23243
23243
|
{ ...betas?.toString() != null ? { "anthropic-beta": betas?.toString() } : void 0 },
|
|
@@ -23586,8 +23586,8 @@ var init_client = __esm({
|
|
|
23586
23586
|
}
|
|
23587
23587
|
return buildHeaders([{ Authorization: `Bearer ${this.authToken}` }]);
|
|
23588
23588
|
}
|
|
23589
|
-
stringifyQuery(
|
|
23590
|
-
return stringifyQuery(
|
|
23589
|
+
stringifyQuery(query2) {
|
|
23590
|
+
return stringifyQuery(query2);
|
|
23591
23591
|
}
|
|
23592
23592
|
getUserAgent() {
|
|
23593
23593
|
return `${this.constructor.name}/JS ${VERSION}`;
|
|
@@ -23598,16 +23598,16 @@ var init_client = __esm({
|
|
|
23598
23598
|
makeStatusError(status, error, message, headers) {
|
|
23599
23599
|
return APIError.generate(status, error, message, headers);
|
|
23600
23600
|
}
|
|
23601
|
-
buildURL(path5,
|
|
23601
|
+
buildURL(path5, query2, defaultBaseURL) {
|
|
23602
23602
|
const baseURL = !__classPrivateFieldGet(this, _BaseAnthropic_instances, "m", _BaseAnthropic_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
|
|
23603
23603
|
const url = isAbsoluteURL(path5) ? new URL(path5) : new URL(baseURL + (baseURL.endsWith("/") && path5.startsWith("/") ? path5.slice(1) : path5));
|
|
23604
23604
|
const defaultQuery = this.defaultQuery();
|
|
23605
23605
|
const pathQuery = Object.fromEntries(url.searchParams);
|
|
23606
23606
|
if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
|
|
23607
|
-
|
|
23607
|
+
query2 = { ...pathQuery, ...defaultQuery, ...query2 };
|
|
23608
23608
|
}
|
|
23609
|
-
if (typeof
|
|
23610
|
-
url.search = this.stringifyQuery(
|
|
23609
|
+
if (typeof query2 === "object" && query2 && !Array.isArray(query2)) {
|
|
23610
|
+
url.search = this.stringifyQuery(query2);
|
|
23611
23611
|
}
|
|
23612
23612
|
return url.toString();
|
|
23613
23613
|
}
|
|
@@ -23920,14 +23920,14 @@ var init_client = __esm({
|
|
|
23920
23920
|
}
|
|
23921
23921
|
async buildRequest(inputOptions, { retryCount = 0 } = {}) {
|
|
23922
23922
|
const options = { ...inputOptions };
|
|
23923
|
-
const { method, path: path5, query, defaultBaseURL } = options;
|
|
23923
|
+
const { method, path: path5, query: query2, defaultBaseURL } = options;
|
|
23924
23924
|
if (this._authState.resolution) {
|
|
23925
23925
|
await this._authState.resolution;
|
|
23926
23926
|
}
|
|
23927
23927
|
if (!this._baseURLIsExplicit && this._authState.baseURL && this.baseURL !== this._authState.baseURL) {
|
|
23928
23928
|
this.baseURL = this._authState.baseURL;
|
|
23929
23929
|
}
|
|
23930
|
-
const url = this.buildURL(path5,
|
|
23930
|
+
const url = this.buildURL(path5, query2, defaultBaseURL);
|
|
23931
23931
|
if ("timeout" in options)
|
|
23932
23932
|
validatePositiveInteger("timeout", options.timeout);
|
|
23933
23933
|
options.timeout = options.timeout ?? this.timeout;
|
|
@@ -24045,8 +24045,8 @@ function stripFallbackBlocks(body) {
|
|
|
24045
24045
|
function betaRefusalFallbackMiddleware(fallbacks, options = {}) {
|
|
24046
24046
|
let warnedMissingState = false;
|
|
24047
24047
|
return async (request, next, ctx) => {
|
|
24048
|
-
const [path5,
|
|
24049
|
-
if (fallbacks.length === 0 || ctx.options?.method !== "post" || path5 !== "/v1/messages" || new URLSearchParams(
|
|
24048
|
+
const [path5, query2] = (ctx.options?.path ?? "").split("?");
|
|
24049
|
+
if (fallbacks.length === 0 || ctx.options?.method !== "post" || path5 !== "/v1/messages" || new URLSearchParams(query2).get("beta") !== "true" || typeof ctx.options.body !== "object" || ctx.options.body == null) {
|
|
24050
24050
|
return next(request);
|
|
24051
24051
|
}
|
|
24052
24052
|
if (ctx.options.body.fallbacks != null) {
|
|
@@ -25973,7 +25973,7 @@ function splitProgressChunk(chunk) {
|
|
|
25973
25973
|
}
|
|
25974
25974
|
function shStream(cmd, args, opts = {}) {
|
|
25975
25975
|
const cap = opts.maxStderrBytes ?? 64 * 1024;
|
|
25976
|
-
return new Promise((
|
|
25976
|
+
return new Promise((resolve5, reject) => {
|
|
25977
25977
|
void (async () => {
|
|
25978
25978
|
const spawn6 = opts.spawnFn ?? (await import("child_process")).spawn;
|
|
25979
25979
|
const child = spawn6(cmd, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -25991,7 +25991,7 @@ function shStream(cmd, args, opts = {}) {
|
|
|
25991
25991
|
child.on("error", (err) => reject(err));
|
|
25992
25992
|
child.on("close", (code, signal) => {
|
|
25993
25993
|
if (code === 0) {
|
|
25994
|
-
|
|
25994
|
+
resolve5({ stdout: stdout.trim(), stderr });
|
|
25995
25995
|
return;
|
|
25996
25996
|
}
|
|
25997
25997
|
const err = new Error(
|
|
@@ -26058,6 +26058,36 @@ function isCommandUnavailable(facts, countsRead) {
|
|
|
26058
26058
|
return false;
|
|
26059
26059
|
return facts.stdout.trim().length === 0 && EXEC_FAILURE.test(facts.stderr);
|
|
26060
26060
|
}
|
|
26061
|
+
function isMissingSystemDependency(facts, countsRead) {
|
|
26062
|
+
if (countsRead)
|
|
26063
|
+
return null;
|
|
26064
|
+
if (facts.exitCode === 0 || facts.exitCode === null)
|
|
26065
|
+
return null;
|
|
26066
|
+
const out = `${facts.stdout}
|
|
26067
|
+
${facts.stderr}`;
|
|
26068
|
+
for (const pattern of MISSING_SYSTEM_DEPENDENCY) {
|
|
26069
|
+
const hit = pattern.exec(out);
|
|
26070
|
+
if (hit)
|
|
26071
|
+
return hit[0];
|
|
26072
|
+
}
|
|
26073
|
+
return null;
|
|
26074
|
+
}
|
|
26075
|
+
function isOfflineBuildGap(facts, counts) {
|
|
26076
|
+
if (facts.runtime !== "jvm")
|
|
26077
|
+
return null;
|
|
26078
|
+
if (counts !== null && counts.tests_failed > 0)
|
|
26079
|
+
return null;
|
|
26080
|
+
if (facts.exitCode === 0 || facts.exitCode === null)
|
|
26081
|
+
return null;
|
|
26082
|
+
const out = `${facts.stdout}
|
|
26083
|
+
${facts.stderr}`;
|
|
26084
|
+
for (const pattern of OFFLINE_BUILD_GAP) {
|
|
26085
|
+
const hit = pattern.exec(out);
|
|
26086
|
+
if (hit)
|
|
26087
|
+
return hit[0];
|
|
26088
|
+
}
|
|
26089
|
+
return null;
|
|
26090
|
+
}
|
|
26061
26091
|
function classifyVerification(facts) {
|
|
26062
26092
|
if (facts.timedOut) {
|
|
26063
26093
|
return {
|
|
@@ -26082,6 +26112,22 @@ function classifyVerification(facts) {
|
|
|
26082
26112
|
reason: `the test command could not be invoked (exit ${String(facts.exitCode)}). This is an environment failure on our side \u2014 a missing interpreter, a binary outside PATH, or a dependency installed somewhere the command cannot see it. The repo has not been judged.`
|
|
26083
26113
|
};
|
|
26084
26114
|
}
|
|
26115
|
+
const missing = isMissingSystemDependency(facts, counts !== null);
|
|
26116
|
+
if (missing !== null) {
|
|
26117
|
+
return {
|
|
26118
|
+
outcome: "test-command-unavailable",
|
|
26119
|
+
counts,
|
|
26120
|
+
reason: `the build needs a system library or header our image does not carry (${missing}). This is a gap in our environment, not a failure in the repo. The repo has not been judged.`
|
|
26121
|
+
};
|
|
26122
|
+
}
|
|
26123
|
+
const gap = isOfflineBuildGap(facts, counts);
|
|
26124
|
+
if (gap !== null) {
|
|
26125
|
+
return {
|
|
26126
|
+
outcome: "test-command-unavailable",
|
|
26127
|
+
counts,
|
|
26128
|
+
reason: `the build stopped on something our install step did not fetch (${gap}). The test step has no network, so this is a gap in our environment, not a failure in the repo. The repo has not been judged.`
|
|
26129
|
+
};
|
|
26130
|
+
}
|
|
26085
26131
|
if (counts !== null) {
|
|
26086
26132
|
if (counts.tests_failed > 0) {
|
|
26087
26133
|
return {
|
|
@@ -26134,12 +26180,21 @@ function isGreen(outcome) {
|
|
|
26134
26180
|
return outcome === "completed";
|
|
26135
26181
|
}
|
|
26136
26182
|
function isOurFault(outcome) {
|
|
26137
|
-
return outcome
|
|
26183
|
+
return OUR_FAULT[outcome];
|
|
26138
26184
|
}
|
|
26139
|
-
var int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE;
|
|
26185
|
+
var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, OUR_FAULT;
|
|
26140
26186
|
var init_classify2 = __esm({
|
|
26141
26187
|
"../../packages/envrun/dist/classify.js"() {
|
|
26142
26188
|
"use strict";
|
|
26189
|
+
VERIFICATION_OUTCOMES = [
|
|
26190
|
+
"completed",
|
|
26191
|
+
"tests-failed",
|
|
26192
|
+
"no-tests-observed",
|
|
26193
|
+
"counts-unparsed",
|
|
26194
|
+
"test-command-unavailable",
|
|
26195
|
+
"environment-exhausted",
|
|
26196
|
+
"budget-exceeded"
|
|
26197
|
+
];
|
|
26143
26198
|
int = (m, i = 1) => m ? Number(m[i]) : 0;
|
|
26144
26199
|
withSuiteFailures = (counts, suiteLine) => {
|
|
26145
26200
|
if (counts.tests_failed > 0 || !suiteLine)
|
|
@@ -26239,6 +26294,68 @@ var init_classify2 = __esm({
|
|
|
26239
26294
|
return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
|
|
26240
26295
|
}
|
|
26241
26296
|
},
|
|
26297
|
+
{
|
|
26298
|
+
// Maven surefire's per-module summary (TERM-1122), measured against surefire
|
|
26299
|
+
// 3.2.5 through `th run`:
|
|
26300
|
+
//
|
|
26301
|
+
// [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
|
26302
|
+
//
|
|
26303
|
+
// The per-CLASS lines carry the same counts followed by `, Time elapsed: …`, so
|
|
26304
|
+
// the end anchor after `Skipped` is what keeps them out; counting both would
|
|
26305
|
+
// double every test. One summary prints per module, so they are SUMMED. An
|
|
26306
|
+
// error is a test that could not run to an assertion, counted as failed so a
|
|
26307
|
+
// green verdict is impossible while one is outstanding, as pytest's are.
|
|
26308
|
+
runner: "maven surefire",
|
|
26309
|
+
read: (out) => {
|
|
26310
|
+
const lines = [
|
|
26311
|
+
...out.matchAll(/^(?:\[(?:INFO|WARNING|ERROR)\] )?Tests run: (\d+), Failures: (\d+), Errors: (\d+), Skipped: (\d+)(?:, Flakes: \d+)?\s*$/gm)
|
|
26312
|
+
];
|
|
26313
|
+
if (lines.length === 0)
|
|
26314
|
+
return null;
|
|
26315
|
+
let passed = 0;
|
|
26316
|
+
let failed = 0;
|
|
26317
|
+
for (const m of lines) {
|
|
26318
|
+
const [run2, failures, errors, skipped] = [m[1], m[2], m[3], m[4]].map(Number);
|
|
26319
|
+
failed += failures + errors;
|
|
26320
|
+
passed += Math.max(0, run2 - failures - errors - skipped);
|
|
26321
|
+
}
|
|
26322
|
+
return { tests_passed: passed, tests_failed: failed };
|
|
26323
|
+
}
|
|
26324
|
+
},
|
|
26325
|
+
{
|
|
26326
|
+
// Gradle prints no test counts when every test passes, so envrun installs an
|
|
26327
|
+
// init script that prints one line per Test task (`gradleTestSummary.ts`,
|
|
26328
|
+
// TERM-1122). The marker is ours; its format is fixed there.
|
|
26329
|
+
runner: "gradle",
|
|
26330
|
+
read: (out) => {
|
|
26331
|
+
const lines = [
|
|
26332
|
+
...out.matchAll(/^terminalhire-gradle-tests: task=\S+ total=(\d+) passed=(\d+) failed=(\d+) skipped=(\d+)\s*$/gm)
|
|
26333
|
+
];
|
|
26334
|
+
if (lines.length === 0)
|
|
26335
|
+
return null;
|
|
26336
|
+
return {
|
|
26337
|
+
tests_passed: lines.reduce((sum, m) => sum + Number(m[2]), 0),
|
|
26338
|
+
tests_failed: lines.reduce((sum, m) => sum + Number(m[3]), 0)
|
|
26339
|
+
};
|
|
26340
|
+
}
|
|
26341
|
+
},
|
|
26342
|
+
{
|
|
26343
|
+
// CTest's closing summary: `50% tests passed, 1 tests failed out of 2`. Measured
|
|
26344
|
+
// on cmake 3.25 in the cpp image (TERM-1123); the singular `1 test failed` is
|
|
26345
|
+
// CTest's own form for one. Anchored at line start, so a log line quoting it
|
|
26346
|
+
// is not read as the summary. Meson and a bare `make test` print no summary we
|
|
26347
|
+
// can trust and stay unparsed.
|
|
26348
|
+
runner: "ctest",
|
|
26349
|
+
read: (out) => {
|
|
26350
|
+
if (/^No tests were found!!!$/m.test(out))
|
|
26351
|
+
return { tests_passed: 0, tests_failed: 0 };
|
|
26352
|
+
const m = /^\d+% tests passed, (\d+) tests? failed out of (\d+)$/m.exec(out);
|
|
26353
|
+
if (!m)
|
|
26354
|
+
return null;
|
|
26355
|
+
const failed = Number(m[1]);
|
|
26356
|
+
return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
|
|
26357
|
+
}
|
|
26358
|
+
},
|
|
26242
26359
|
{
|
|
26243
26360
|
// mocha: ` 440 passing (1s)` and ` 2 failing`
|
|
26244
26361
|
runner: "mocha",
|
|
@@ -26367,6 +26484,35 @@ var init_classify2 = __esm({
|
|
|
26367
26484
|
"\\d+\\s+examples?,\\s+(?!0\\b)\\d+\\s+failures?"
|
|
26368
26485
|
// rspec's summary line
|
|
26369
26486
|
].join("|"), "m");
|
|
26487
|
+
MISSING_SYSTEM_DEPENDENCY = [
|
|
26488
|
+
/^(?:\S*\/)?ld(?:\.\w+)?: cannot find -l[\w.+-]+/m,
|
|
26489
|
+
/^\S+:\d+(?::\d+)?: fatal error: [\w./+-]+\.(?:h|hh|hpp|hxx): No such file or directory$/m,
|
|
26490
|
+
/^\S+: error while loading shared libraries: lib[\w.+-]*: cannot open shared object file/m
|
|
26491
|
+
];
|
|
26492
|
+
OFFLINE_BUILD_GAP = [
|
|
26493
|
+
// Maven, `-o`: `Cannot access central (…) in offline mode and the artifact … has
|
|
26494
|
+
// not been downloaded from it before.`
|
|
26495
|
+
/in offline mode and the artifact \S+ has not been downloaded from it before/,
|
|
26496
|
+
// Maven without `-o`, as a CI-derived `mvn test` runs: it tries the network.
|
|
26497
|
+
/Could not transfer artifact \S+ from\/to /,
|
|
26498
|
+
/Plugin \S+ or one of its dependencies could not be resolved/,
|
|
26499
|
+
// Gradle, `--offline`.
|
|
26500
|
+
/No cached version (?:listing )?(?:of|for) \S+ available for offline mode/,
|
|
26501
|
+
// Gradle without `--offline`, and the wrapper fetching its own distribution.
|
|
26502
|
+
/Could not GET 'https?:\/\//,
|
|
26503
|
+
/^Downloading https:\/\/services\.gradle\.org\/distributions\//m,
|
|
26504
|
+
/No matching toolchains found for requested specification/,
|
|
26505
|
+
/Cannot find a Java installation on your machine matching/
|
|
26506
|
+
];
|
|
26507
|
+
OUR_FAULT = {
|
|
26508
|
+
completed: false,
|
|
26509
|
+
"tests-failed": false,
|
|
26510
|
+
"no-tests-observed": false,
|
|
26511
|
+
"budget-exceeded": false,
|
|
26512
|
+
"counts-unparsed": true,
|
|
26513
|
+
"test-command-unavailable": true,
|
|
26514
|
+
"environment-exhausted": true
|
|
26515
|
+
};
|
|
26370
26516
|
}
|
|
26371
26517
|
});
|
|
26372
26518
|
|
|
@@ -26400,7 +26546,8 @@ function scrubEnv(source, opts) {
|
|
|
26400
26546
|
if (!tmpDir.startsWith("/")) {
|
|
26401
26547
|
throw new SandboxEnvError(`tmpDir must be an absolute path, got ${JSON.stringify(tmpDir)}`);
|
|
26402
26548
|
}
|
|
26403
|
-
|
|
26549
|
+
const homesToCompare = opts.jailHomeNamespace === "guest" ? [] : realHomeCandidates(source);
|
|
26550
|
+
for (const realHome of homesToCompare) {
|
|
26404
26551
|
if (jailHome === realHome || jailHome === `${realHome}/`) {
|
|
26405
26552
|
throw new SandboxEnvError("jailHome is the real HOME \u2014 the sandbox would read the user profile");
|
|
26406
26553
|
}
|
|
@@ -26432,6 +26579,8 @@ function scrubEnv(source, opts) {
|
|
|
26432
26579
|
env["GOCACHE"] = join18(jailHome, ".cache", "go-build");
|
|
26433
26580
|
env["GOFLAGS"] = "-modcacherw";
|
|
26434
26581
|
env["CARGO_HOME"] = join18(jailHome, ".cargo");
|
|
26582
|
+
env["MAVEN_OPTS"] = `-Dmaven.repo.local=${join18(jailHome, ".m2", "repository")}`;
|
|
26583
|
+
env["GRADLE_USER_HOME"] = join18(jailHome, ".gradle");
|
|
26435
26584
|
const bundle = join18(jailHome, ".bundle");
|
|
26436
26585
|
env["GEM_HOME"] = bundle;
|
|
26437
26586
|
env["BUNDLE_PATH"] = bundle;
|
|
@@ -26487,8 +26636,8 @@ var init_reap = __esm({
|
|
|
26487
26636
|
|
|
26488
26637
|
// ../../packages/containment/dist/fence.js
|
|
26489
26638
|
import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
|
|
26490
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync3, realpathSync, writeFileSync as writeFileSync10 } from "fs";
|
|
26491
|
-
import { dirname as dirname6, isAbsolute as isAbsolute3, join as join19, posix as posix2 } from "path";
|
|
26639
|
+
import { existsSync as existsSync9, lchownSync, lstatSync as lstatSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, realpathSync, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
26640
|
+
import { basename as basename4, dirname as dirname6, isAbsolute as isAbsolute3, join as join19, posix as posix2, resolve as resolve4, sep as sep5 } from "path";
|
|
26492
26641
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
26493
26642
|
function canonical(path5, label) {
|
|
26494
26643
|
if (!isAbsolute3(path5)) {
|
|
@@ -26515,7 +26664,7 @@ function resolverFor(domain) {
|
|
|
26515
26664
|
function pathDomainOf(spec) {
|
|
26516
26665
|
return spec.pathDomain ?? "local";
|
|
26517
26666
|
}
|
|
26518
|
-
function jailPasswd(uid =
|
|
26667
|
+
function jailPasswd(uid = localGuestUser()?.uid ?? null, gid = localGuestUser()?.gid ?? null) {
|
|
26519
26668
|
const rows = ["root:x:0:0:root:/root:/usr/sbin/nologin"];
|
|
26520
26669
|
if (uid !== null && gid !== null && uid !== 0) {
|
|
26521
26670
|
rows.push(`${FENCE_USER}:x:${uid}:${gid}:fenced build user:${GUEST_JAIL}:/usr/sbin/nologin`);
|
|
@@ -26524,7 +26673,7 @@ function jailPasswd(uid = idOrNull("getuid"), gid = idOrNull("getgid")) {
|
|
|
26524
26673
|
return `${rows.join("\n")}
|
|
26525
26674
|
`;
|
|
26526
26675
|
}
|
|
26527
|
-
function jailGroup(gid =
|
|
26676
|
+
function jailGroup(gid = localGuestUser()?.gid ?? null) {
|
|
26528
26677
|
const rows = ["root:x:0:"];
|
|
26529
26678
|
if (gid !== null && gid !== 0)
|
|
26530
26679
|
rows.push(`${FENCE_USER}:x:${gid}:`);
|
|
@@ -26536,19 +26685,139 @@ function idOrNull(fn) {
|
|
|
26536
26685
|
const f = process[fn];
|
|
26537
26686
|
return typeof f === "function" ? f.call(process) : null;
|
|
26538
26687
|
}
|
|
26539
|
-
function
|
|
26688
|
+
function hostIds() {
|
|
26689
|
+
const uid = idOrNull("getuid");
|
|
26690
|
+
const gid = idOrNull("getgid");
|
|
26691
|
+
return uid === null || gid === null ? null : { uid, gid };
|
|
26692
|
+
}
|
|
26693
|
+
function localGuestUser(ids2 = hostIds()) {
|
|
26694
|
+
if (ids2 === null)
|
|
26695
|
+
return null;
|
|
26696
|
+
return ids2.uid === 0 ? REMAPPED_GUEST_USER : ids2;
|
|
26697
|
+
}
|
|
26698
|
+
function chownTree(dir, user) {
|
|
26699
|
+
const stack = [dir];
|
|
26700
|
+
for (let p = stack.pop(); p !== void 0; p = stack.pop()) {
|
|
26701
|
+
lchownSync(p, user.uid, user.gid);
|
|
26702
|
+
if (lstatSync3(p).isDirectory()) {
|
|
26703
|
+
for (const entry of readdirSync2(p))
|
|
26704
|
+
stack.push(join19(p, entry));
|
|
26705
|
+
}
|
|
26706
|
+
}
|
|
26707
|
+
}
|
|
26708
|
+
function canonicalPath(p) {
|
|
26709
|
+
let head = resolve4(p);
|
|
26710
|
+
const tail2 = [];
|
|
26711
|
+
for (; ; ) {
|
|
26712
|
+
try {
|
|
26713
|
+
return join19(realpathSync.native(head), ...tail2.reverse());
|
|
26714
|
+
} catch {
|
|
26715
|
+
const up = dirname6(head);
|
|
26716
|
+
if (up === head)
|
|
26717
|
+
return resolve4(p);
|
|
26718
|
+
tail2.push(basename4(head));
|
|
26719
|
+
head = up;
|
|
26720
|
+
}
|
|
26721
|
+
}
|
|
26722
|
+
}
|
|
26723
|
+
function statOrAbsent(p, fn) {
|
|
26724
|
+
try {
|
|
26725
|
+
return fn();
|
|
26726
|
+
} catch (err) {
|
|
26727
|
+
const code = err.code;
|
|
26728
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
26729
|
+
return void 0;
|
|
26730
|
+
throw new FenceError(`cannot stat ${JSON.stringify(p)}: ${err.message}`, {
|
|
26731
|
+
cause: err
|
|
26732
|
+
});
|
|
26733
|
+
}
|
|
26734
|
+
}
|
|
26735
|
+
function fileIdentity(p) {
|
|
26736
|
+
const st = statOrAbsent(p, () => statSync3(p, { bigint: true }));
|
|
26737
|
+
return st === void 0 ? null : { dev: st.dev, ino: st.ino };
|
|
26738
|
+
}
|
|
26739
|
+
function isOrContains(outer, inner) {
|
|
26740
|
+
const fold = process.platform === "win32" ? (s) => s.toLowerCase() : (s) => s;
|
|
26741
|
+
const o = fold(canonicalPath(outer));
|
|
26742
|
+
const i = fold(canonicalPath(inner));
|
|
26743
|
+
const prefix = o.endsWith(sep5) ? o : o + sep5;
|
|
26744
|
+
if (i === o || i.startsWith(prefix))
|
|
26745
|
+
return true;
|
|
26746
|
+
const target = fileIdentity(outer);
|
|
26747
|
+
if (target === null || target.ino === 0n)
|
|
26748
|
+
return false;
|
|
26749
|
+
for (const start of physicalSpellings(canonicalPath(inner))) {
|
|
26750
|
+
for (let a = start; ; a = dirname6(a)) {
|
|
26751
|
+
const id = fileIdentity(a);
|
|
26752
|
+
if (id !== null && id.ino !== 0n && id.dev === target.dev && id.ino === target.ino) {
|
|
26753
|
+
return true;
|
|
26754
|
+
}
|
|
26755
|
+
if (dirname6(a) === a)
|
|
26756
|
+
break;
|
|
26757
|
+
}
|
|
26758
|
+
}
|
|
26759
|
+
return false;
|
|
26760
|
+
}
|
|
26761
|
+
function physicalSpellings(p) {
|
|
26762
|
+
if (process.platform !== "darwin" || p.startsWith(DARWIN_DATA_VOLUME + sep5))
|
|
26763
|
+
return [p];
|
|
26764
|
+
const onData = DARWIN_DATA_VOLUME + p;
|
|
26765
|
+
const logical = fileIdentity(p);
|
|
26766
|
+
const physical = fileIdentity(onData);
|
|
26767
|
+
const same = logical !== null && physical !== null && logical.dev === physical.dev && logical.ino === physical.ino;
|
|
26768
|
+
return same ? [p, onData] : [p];
|
|
26769
|
+
}
|
|
26770
|
+
function realHomes(who) {
|
|
26771
|
+
try {
|
|
26772
|
+
return realHomeCandidates(process.env);
|
|
26773
|
+
} catch (err) {
|
|
26774
|
+
throw new FenceError(`${who}: ${err.message}`, { cause: err });
|
|
26775
|
+
}
|
|
26776
|
+
}
|
|
26777
|
+
function assertHoldsNoRealHome(path5, what, who) {
|
|
26778
|
+
for (const home of realHomes(who)) {
|
|
26779
|
+
if (isOrContains(path5, home)) {
|
|
26780
|
+
throw new FenceError(`${who}: ${what} ${JSON.stringify(path5)} is or contains the real home ${JSON.stringify(home)} \u2014 the jail would expose the developer's profile`);
|
|
26781
|
+
}
|
|
26782
|
+
}
|
|
26783
|
+
}
|
|
26784
|
+
function assertNoForeignLinks(paths) {
|
|
26785
|
+
for (const p of paths) {
|
|
26786
|
+
const st = statOrAbsent(p, () => lstatSync3(p));
|
|
26787
|
+
if (st?.isSymbolicLink() === true) {
|
|
26788
|
+
throw new FenceError(`buildJail: ${JSON.stringify(p)} is a pre-existing symlink \u2014 buildJail would write through it, and a jail directory is never a link`);
|
|
26789
|
+
}
|
|
26790
|
+
if (st?.isFile() === true && st.nlink > 1) {
|
|
26791
|
+
throw new FenceError(`buildJail: ${JSON.stringify(p)} is a pre-existing file with ${String(st.nlink)} hard links \u2014 buildJail would overwrite an inode another path shares`);
|
|
26792
|
+
}
|
|
26793
|
+
}
|
|
26794
|
+
}
|
|
26795
|
+
function buildJail(root, guestUser, io = {}) {
|
|
26796
|
+
const ids2 = io.ids === void 0 ? hostIds() : io.ids;
|
|
26797
|
+
const guest = guestUser ?? localGuestUser(ids2);
|
|
26540
26798
|
const jail = join19(root, JAIL_SEGMENT);
|
|
26541
26799
|
const tmp = join19(root, JAIL_TMP_SEGMENT);
|
|
26542
|
-
|
|
26800
|
+
const dirs = [jail, tmp, join19(jail, ".config"), join19(jail, ".cache"), join19(jail, ".npm")];
|
|
26801
|
+
const files = [".npmrc", JAIL_PASSWD_FILE, JAIL_GROUP_FILE, ".gitconfig"].map((f) => join19(jail, f));
|
|
26802
|
+
assertHoldsNoRealHome(root, "the jail root", "buildJail");
|
|
26803
|
+
assertNoForeignLinks([...dirs, ...files]);
|
|
26804
|
+
assertHoldsNoRealHome(jail, "the jail directory", "buildJail");
|
|
26805
|
+
assertHoldsNoRealHome(tmp, "the jail tmp directory", "buildJail");
|
|
26806
|
+
for (const dir of dirs) {
|
|
26543
26807
|
mkdirSync3(dir, { recursive: true });
|
|
26544
26808
|
}
|
|
26545
26809
|
writeFileSync10(join19(jail, ".npmrc"), "", "utf8");
|
|
26546
|
-
writeFileSync10(join19(jail, JAIL_PASSWD_FILE),
|
|
26547
|
-
writeFileSync10(join19(jail, JAIL_GROUP_FILE),
|
|
26810
|
+
writeFileSync10(join19(jail, JAIL_PASSWD_FILE), guest ? jailPasswd(guest.uid, guest.gid) : jailPasswd(null, null), "utf8");
|
|
26811
|
+
writeFileSync10(join19(jail, JAIL_GROUP_FILE), guest ? jailGroup(guest.gid) : jailGroup(null), "utf8");
|
|
26548
26812
|
writeFileSync10(join19(jail, ".gitconfig"), '[user]\n name = sandbox\n email = sandbox@localhost\n[safe]\n directory = *\n[url "https://github.com/"]\n insteadOf = ssh://git@github.com/\n insteadOf = git@github.com:\n', "utf8");
|
|
26813
|
+
if (ids2?.uid === 0 && guest && guest.uid !== 0) {
|
|
26814
|
+
const chown = io.chown ?? chownTree;
|
|
26815
|
+
chown(jail, guest);
|
|
26816
|
+
chown(tmp, guest);
|
|
26817
|
+
}
|
|
26549
26818
|
return { jail, tmp };
|
|
26550
26819
|
}
|
|
26551
|
-
var FenceError, ContainmentError, ContainmentRefusalError, JAIL_PASSWD_FILE, JAIL_GROUP_FILE, GUEST_JAIL, FENCE_USER, JAIL_SEGMENT, JAIL_TMP_SEGMENT;
|
|
26820
|
+
var FenceError, ContainmentError, ContainmentRefusalError, JAIL_PASSWD_FILE, JAIL_GROUP_FILE, GUEST_JAIL, FENCE_USER, REMAPPED_GUEST_USER, JAIL_SEGMENT, JAIL_TMP_SEGMENT, DARWIN_DATA_VOLUME;
|
|
26552
26821
|
var init_fence = __esm({
|
|
26553
26822
|
"../../packages/containment/dist/fence.js"() {
|
|
26554
26823
|
"use strict";
|
|
@@ -26569,8 +26838,10 @@ var init_fence = __esm({
|
|
|
26569
26838
|
JAIL_GROUP_FILE = ".fence-group";
|
|
26570
26839
|
GUEST_JAIL = "/fenced/jail";
|
|
26571
26840
|
FENCE_USER = "fenced";
|
|
26841
|
+
REMAPPED_GUEST_USER = { uid: 1e3, gid: 1e3 };
|
|
26572
26842
|
JAIL_SEGMENT = "jail";
|
|
26573
26843
|
JAIL_TMP_SEGMENT = "tmp";
|
|
26844
|
+
DARWIN_DATA_VOLUME = "/System/Volumes/Data";
|
|
26574
26845
|
}
|
|
26575
26846
|
});
|
|
26576
26847
|
|
|
@@ -26723,7 +26994,27 @@ var init_dockerClient = __esm({
|
|
|
26723
26994
|
// ../../packages/containment/dist/egressProxy.js
|
|
26724
26995
|
import net from "net";
|
|
26725
26996
|
import http from "http";
|
|
26726
|
-
|
|
26997
|
+
function parseDeniedHosts(output) {
|
|
26998
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26999
|
+
for (const line of output.split(/\r?\n/)) {
|
|
27000
|
+
if (!line.startsWith(DENIAL_PREFIX))
|
|
27001
|
+
continue;
|
|
27002
|
+
let parsed;
|
|
27003
|
+
try {
|
|
27004
|
+
parsed = JSON.parse(line.slice(DENIAL_PREFIX.length));
|
|
27005
|
+
} catch {
|
|
27006
|
+
continue;
|
|
27007
|
+
}
|
|
27008
|
+
const host = parsed?.host;
|
|
27009
|
+
if (typeof host !== "string")
|
|
27010
|
+
continue;
|
|
27011
|
+
const h = host.toLowerCase().replace(/\.$/, "");
|
|
27012
|
+
if (HOSTNAME.test(h))
|
|
27013
|
+
seen.add(h);
|
|
27014
|
+
}
|
|
27015
|
+
return [...seen];
|
|
27016
|
+
}
|
|
27017
|
+
var DEFAULT_INSTALL_ALLOWLIST, DENIAL_PREFIX, HOSTNAME;
|
|
26727
27018
|
var init_egressProxy = __esm({
|
|
26728
27019
|
"../../packages/containment/dist/egressProxy.js"() {
|
|
26729
27020
|
"use strict";
|
|
@@ -26749,6 +27040,18 @@ var init_egressProxy = __esm({
|
|
|
26749
27040
|
// NuGet; the coupling test refuses a mapped runtime whose registry is unreachable, and
|
|
26750
27041
|
// it caught this one the moment the image landed.
|
|
26751
27042
|
"api.nuget.org",
|
|
27043
|
+
// TERM-1122, added with the `jvm` image mapping, each from a MEASURED denial
|
|
27044
|
+
// through `th run` (the proxy's 403 in the build's own output), not a guess:
|
|
27045
|
+
// - Maven Central, where both `mvn` and Gradle's `mavenCentral()` resolve.
|
|
27046
|
+
// - `services.gradle.org`, where a Gradle wrapper fetches its distribution.
|
|
27047
|
+
// - `release-assets.githubusercontent.com`, where that fetch LANDS. Measured
|
|
27048
|
+
// chain: services.gradle.org 307 -> github.com 302 -> this host 200. The
|
|
27049
|
+
// `github.com` note below keeps `githubusercontent.com` denied until a real
|
|
27050
|
+
// install needs it; this is that install. Only this one host: raw blobs and
|
|
27051
|
+
// `objects.githubusercontent.com` stay denied.
|
|
27052
|
+
"repo.maven.apache.org",
|
|
27053
|
+
"services.gradle.org",
|
|
27054
|
+
"release-assets.githubusercontent.com",
|
|
26752
27055
|
// Git dependencies. Approved by Eric 2026-07-22 after a live run measured the
|
|
26753
27056
|
// chokepoint working under load (20 events, 16 allowed / 4 denied by host).
|
|
26754
27057
|
//
|
|
@@ -26760,24 +27063,47 @@ var init_egressProxy = __esm({
|
|
|
26760
27063
|
// allowlist than breadth.
|
|
26761
27064
|
//
|
|
26762
27065
|
// What it does NOT grant: `githubusercontent.com` is a DIFFERENT domain, so
|
|
26763
|
-
//
|
|
26764
|
-
//
|
|
27066
|
+
// raw blobs and `objects.githubusercontent.com` stay denied; only
|
|
27067
|
+
// `release-assets.githubusercontent.com` is listed, above, for the Gradle
|
|
27068
|
+
// wrapper. If a real install needs more, that should arrive as a measured
|
|
27069
|
+
// egress denial, not a pre-emptive guess.
|
|
27070
|
+
//
|
|
27071
|
+
// WHO RUNS INSTALL SCRIPTS THROUGH THIS GRANT (TERM-1157). This comment used
|
|
27072
|
+
// to say install "already runs with `--ignore-scripts`". That is true of
|
|
27073
|
+
// `apps/merge-agent` (`src/sandbox/plan.ts` adds the flag on every branch)
|
|
27074
|
+
// and false of `th run`: `packages/envspec/src/manifest.ts` derives a plain
|
|
27075
|
+
// `npm ci`, so there every dependency's `preinstall`/`install`/`postinstall`
|
|
27076
|
+
// executes behind this proxy. TERM-1157 chose to keep them running.
|
|
26765
27077
|
//
|
|
26766
|
-
//
|
|
26767
|
-
//
|
|
26768
|
-
//
|
|
27078
|
+
// For `th run`, then, the grant reaches code the repo's dependencies chose,
|
|
27079
|
+
// not only the package manager. What bounds it:
|
|
27080
|
+
// - no credential of ours is in the fence: `auditEnv` (`env.ts`) refuses to
|
|
27081
|
+
// spawn when a variable's name or a GitHub-token-shaped value says
|
|
27082
|
+
// otherwise, so a script cannot act on GitHub as us;
|
|
27083
|
+
// - the fence holds the repo's tree and its dependencies, nothing else of
|
|
27084
|
+
// ours;
|
|
27085
|
+
// - every step after install runs with no network at all.
|
|
27086
|
+
// What it does NOT bound: a script can download and run any public GitHub
|
|
27087
|
+
// content, and one that brings its author's own token can push what the
|
|
27088
|
+
// fence holds to its author's account. `registry.npmjs.org` above gives the
|
|
27089
|
+
// same script the same reach (download anything, publish with its own
|
|
27090
|
+
// token), so this entry adds a second address for a capability the install
|
|
27091
|
+
// already has rather than a new one. That is the case for keeping it; it is
|
|
27092
|
+
// not a claim that GitHub is read-only from here.
|
|
26769
27093
|
"github.com"
|
|
26770
27094
|
];
|
|
27095
|
+
DENIAL_PREFIX = "proxy-denied ";
|
|
27096
|
+
HOSTNAME = /^[a-z0-9](?:[a-z0-9.-]{0,251}[a-z0-9])?$/;
|
|
26771
27097
|
}
|
|
26772
27098
|
});
|
|
26773
27099
|
|
|
26774
27100
|
// ../../packages/containment/dist/container.js
|
|
26775
27101
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
26776
27102
|
import { dirname as dirname7, join as join20 } from "path";
|
|
26777
|
-
import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync10, mkdtempSync, rmSync as rmSync6 } from "fs";
|
|
27103
|
+
import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync10, mkdtempSync, rmSync as rmSync6, statSync as statSync4 } from "fs";
|
|
26778
27104
|
import { tmpdir } from "os";
|
|
26779
27105
|
function scrubEnvPathsFor(containmentKind, host) {
|
|
26780
|
-
return containmentKind === "container" ? { jailHome: GUEST.jail, tmpDir: GUEST.tmp } : { jailHome: host.jail, tmpDir: host.tmp };
|
|
27106
|
+
return containmentKind === "container" ? { jailHome: GUEST.jail, tmpDir: GUEST.tmp, jailHomeNamespace: "guest" } : { jailHome: host.jail, tmpDir: host.tmp, jailHomeNamespace: "host" };
|
|
26781
27107
|
}
|
|
26782
27108
|
function buildTranslation(spec) {
|
|
26783
27109
|
const roots = [
|
|
@@ -26785,11 +27111,11 @@ function buildTranslation(spec) {
|
|
|
26785
27111
|
{ raw: spec.jail, guest: GUEST.jail, label: "jail" },
|
|
26786
27112
|
{ raw: spec.tmp, guest: GUEST.tmp, label: "tmp" }
|
|
26787
27113
|
];
|
|
26788
|
-
const
|
|
27114
|
+
const resolve5 = resolverFor(pathDomainOf(spec));
|
|
26789
27115
|
const pairs = [];
|
|
26790
27116
|
const seen = /* @__PURE__ */ new Set();
|
|
26791
27117
|
for (const { raw, guest, label } of roots) {
|
|
26792
|
-
for (const host of [raw,
|
|
27118
|
+
for (const host of [raw, resolve5(raw, label)]) {
|
|
26793
27119
|
if (seen.has(host))
|
|
26794
27120
|
continue;
|
|
26795
27121
|
seen.add(host);
|
|
@@ -26872,21 +27198,36 @@ function labelArgs(labels) {
|
|
|
26872
27198
|
}
|
|
26873
27199
|
return out;
|
|
26874
27200
|
}
|
|
26875
|
-
function hostUserFlag() {
|
|
26876
|
-
const
|
|
26877
|
-
|
|
26878
|
-
if (uid === null || gid === null)
|
|
27201
|
+
function hostUserFlag(ids2 = hostIds()) {
|
|
27202
|
+
const guest = localGuestUser(ids2);
|
|
27203
|
+
if (guest === null)
|
|
26879
27204
|
return [];
|
|
26880
|
-
return [`--user=${uid}:${gid}`];
|
|
27205
|
+
return [`--user=${String(guest.uid)}:${String(guest.gid)}`];
|
|
26881
27206
|
}
|
|
26882
|
-
function guestUserFlag(spec) {
|
|
27207
|
+
function guestUserFlag(spec, ids2 = hostIds()) {
|
|
26883
27208
|
const declared = spec.guestUser;
|
|
26884
27209
|
if (declared)
|
|
26885
27210
|
return [`--user=${declared.uid}:${declared.gid}`];
|
|
26886
27211
|
if (pathDomainOf(spec) === "venue") {
|
|
26887
27212
|
throw new FenceError("a venue-domain spec must declare guestUser: the tree is owned by the account that staged it on the venue, and this process\u2019s uid is a fact about a different machine. Handing the guest the local id gives it no write access to its own clone, and that EACCES reaches the classifier as the developer\u2019s suite failing.");
|
|
26888
27213
|
}
|
|
26889
|
-
return hostUserFlag();
|
|
27214
|
+
return hostUserFlag(ids2);
|
|
27215
|
+
}
|
|
27216
|
+
function assertTreeHandedToGuest(spec, ids2 = hostIds(), ownerOf2 = (path5) => statSync4(path5).uid) {
|
|
27217
|
+
if (pathDomainOf(spec) !== "local" || ids2?.uid !== 0)
|
|
27218
|
+
return;
|
|
27219
|
+
const guest = spec.guestUser ?? localGuestUser(ids2);
|
|
27220
|
+
if (guest === null || guest.uid === 0)
|
|
27221
|
+
return;
|
|
27222
|
+
for (const [label, path5] of [
|
|
27223
|
+
["clone", spec.clone],
|
|
27224
|
+
["jail", spec.jail]
|
|
27225
|
+
]) {
|
|
27226
|
+
const owner = ownerOf2(path5);
|
|
27227
|
+
if (owner !== guest.uid) {
|
|
27228
|
+
throw new FenceError(`refusing to run: this process is root, so the guest runs as uid ${String(guest.uid)}, but the ${label} at ${path5} is owned by uid ${String(owner)}. The guest could not write its own tree and the EACCES would read as the developer\u2019s suite failing. Hand it over first (chownTree, or buildJail for the jail).`);
|
|
27229
|
+
}
|
|
27230
|
+
}
|
|
26890
27231
|
}
|
|
26891
27232
|
function validateVolumeName(name, label) {
|
|
26892
27233
|
if (!VOLUME_NAME.test(name) || name.length > VOLUME_NAME_MAX) {
|
|
@@ -26896,14 +27237,17 @@ function validateVolumeName(name, label) {
|
|
|
26896
27237
|
}
|
|
26897
27238
|
function stageMounts(spec) {
|
|
26898
27239
|
const domain = pathDomainOf(spec);
|
|
26899
|
-
const
|
|
27240
|
+
const resolve5 = resolverFor(domain);
|
|
26900
27241
|
const volumes = spec.stageVolumes;
|
|
26901
27242
|
if (domain === "venue") {
|
|
27243
|
+
if (spec.cloneVolume !== void 0) {
|
|
27244
|
+
throw new FenceError("a venue-domain spec must not declare cloneVolume: the venue names both of its volumes in stageVolumes, and a second name for the clone would leave two answers to which one is mounted.");
|
|
27245
|
+
}
|
|
26902
27246
|
if (volumes === void 0) {
|
|
26903
27247
|
throw new FenceError("a venue-domain spec must declare stageVolumes: every writable host path on the venue is mounted noexec, so a bind mount of the staged clone cannot run the binaries an install step downloads (esbuild, swc, sharp, node-gyp \u2014 EACCES). The venue that staged the tree names the volumes it populated; a spec without them would reproduce that EACCES and report it as the developer\u2019s suite failing.");
|
|
26904
27248
|
}
|
|
26905
|
-
|
|
26906
|
-
|
|
27249
|
+
resolve5(spec.clone, "clone");
|
|
27250
|
+
resolve5(spec.jail, "jail");
|
|
26907
27251
|
return [
|
|
26908
27252
|
`--volume=${validateVolumeName(volumes.clone, "the clone volume")}:${GUEST.clone}:rw`,
|
|
26909
27253
|
`--volume=${validateVolumeName(volumes.jail, "the jail volume")}:${GUEST.jail}:rw`
|
|
@@ -26912,20 +27256,24 @@ function stageMounts(spec) {
|
|
|
26912
27256
|
if (volumes !== void 0) {
|
|
26913
27257
|
throw new FenceError("a local-domain spec must not declare stageVolumes: the paths are on this machine and ARE the mount sources, and nothing on the local path populates a volume \u2014 honouring the field would mount an empty clone. Volumes exist for the venue\u2019s noexec host only.");
|
|
26914
27258
|
}
|
|
27259
|
+
const clone = resolve5(spec.clone, "clone");
|
|
27260
|
+
const cloneSource = spec.cloneVolume === void 0 ? clone : validateVolumeName(spec.cloneVolume, "the clone volume");
|
|
27261
|
+
const jail = resolve5(spec.jail, "jail");
|
|
27262
|
+
assertHoldsNoRealHome(jail, "the jail bind source", "stageMounts");
|
|
26915
27263
|
return [
|
|
26916
|
-
`--volume=${
|
|
26917
|
-
`--volume=${
|
|
27264
|
+
`--volume=${cloneSource}:${GUEST.clone}:rw`,
|
|
27265
|
+
`--volume=${jail}:${GUEST.jail}:rw`
|
|
26918
27266
|
];
|
|
26919
27267
|
}
|
|
26920
27268
|
function guestIdentityMounts(spec) {
|
|
26921
27269
|
if (guestUserFlag(spec).length === 0)
|
|
26922
27270
|
return [];
|
|
26923
27271
|
const domain = pathDomainOf(spec);
|
|
26924
|
-
const
|
|
27272
|
+
const resolve5 = resolverFor(domain);
|
|
26925
27273
|
const under = domain === "venue" ? venueJoin : join20;
|
|
26926
|
-
const jail =
|
|
26927
|
-
const passwd =
|
|
26928
|
-
const group =
|
|
27274
|
+
const jail = resolve5(spec.jail, "jail");
|
|
27275
|
+
const passwd = resolve5(under(jail, JAIL_PASSWD_FILE), "the jail passwd file");
|
|
27276
|
+
const group = resolve5(under(jail, JAIL_GROUP_FILE), "the jail group file");
|
|
26929
27277
|
return [`--volume=${passwd}:/etc/passwd:ro`, `--volume=${group}:/etc/group:ro`];
|
|
26930
27278
|
}
|
|
26931
27279
|
function containerArgs(spec, env, opts) {
|
|
@@ -27040,6 +27388,9 @@ function containerArgs(spec, env, opts) {
|
|
|
27040
27388
|
args.push(`--env=${k}=${url}`);
|
|
27041
27389
|
}
|
|
27042
27390
|
args.push("--env=NO_PROXY=", "--env=no_proxy=");
|
|
27391
|
+
const host = opts.net.proxyHost;
|
|
27392
|
+
const port = String(opts.net.proxyPort);
|
|
27393
|
+
args.push(`--env=JAVA_TOOL_OPTIONS=-Dhttp.proxyHost=${host} -Dhttp.proxyPort=${port} -Dhttps.proxyHost=${host} -Dhttps.proxyPort=${port} -Daether.connector.http.useSystemProperties=true`);
|
|
27043
27394
|
}
|
|
27044
27395
|
args.push("--");
|
|
27045
27396
|
args.push(validateImage(opts.image ?? DEFAULT_CONTAINER_IMAGE));
|
|
@@ -27255,7 +27606,14 @@ async function startProxySidecar(d, allow, idBase, staged, labels) {
|
|
|
27255
27606
|
await waitForProxyReady(d, proxyName);
|
|
27256
27607
|
return {
|
|
27257
27608
|
net: { network: netInt, proxyHost: proxyName, proxyPort: SIDECAR_PROXY_PORT },
|
|
27258
|
-
teardown
|
|
27609
|
+
teardown,
|
|
27610
|
+
// A `docker logs` that fails yields no hosts. That loses the hint and
|
|
27611
|
+
// changes nothing else: the list only ever adds words to a reason.
|
|
27612
|
+
deniedHosts: () => {
|
|
27613
|
+
const logs = dockerSync(d, ["logs", proxyName]);
|
|
27614
|
+
return logs.ok ? parseDeniedHosts(`${logs.stdout}
|
|
27615
|
+
${logs.stderr}`) : [];
|
|
27616
|
+
}
|
|
27259
27617
|
};
|
|
27260
27618
|
} catch (err) {
|
|
27261
27619
|
teardown();
|
|
@@ -27321,6 +27679,7 @@ async function runContainedOn(d, spec, env, opts = {}) {
|
|
|
27321
27679
|
assertDomainDeclared(d, spec);
|
|
27322
27680
|
assertNotAmbientlySteered(d, spec);
|
|
27323
27681
|
assertProxyStagedForVenue(spec, opts.proxyCode);
|
|
27682
|
+
assertTreeHandedToGuest(spec);
|
|
27324
27683
|
const leaks = auditEnv(env);
|
|
27325
27684
|
if (leaks.length > 0) {
|
|
27326
27685
|
throw new FenceError(`refusing to spawn: environment carries credential material (${leaks.join(", ")})`);
|
|
@@ -27341,7 +27700,8 @@ async function runContainedOn(d, spec, env, opts = {}) {
|
|
|
27341
27700
|
net: sidecar?.net,
|
|
27342
27701
|
labels: opts.labels
|
|
27343
27702
|
});
|
|
27344
|
-
|
|
27703
|
+
const result = await spawnWorkload(d, idBase, argv, opts.timeoutMs ?? 9e5);
|
|
27704
|
+
return sidecar ? { ...result, egressDenied: sidecar.deniedHosts() } : result;
|
|
27345
27705
|
} finally {
|
|
27346
27706
|
sidecar?.teardown();
|
|
27347
27707
|
}
|
|
@@ -27372,7 +27732,10 @@ var init_container = __esm({
|
|
|
27372
27732
|
"http_proxy",
|
|
27373
27733
|
"https_proxy",
|
|
27374
27734
|
"NO_PROXY",
|
|
27375
|
-
"no_proxy"
|
|
27735
|
+
"no_proxy",
|
|
27736
|
+
// Carries the JVM's copy of the proxy (below), so an inherited value is dropped
|
|
27737
|
+
// for the same reason as the rest.
|
|
27738
|
+
"JAVA_TOOL_OPTIONS"
|
|
27376
27739
|
]);
|
|
27377
27740
|
WINDOWS_DRIVE_ROOT = /^[A-Za-z]:[\\/]/;
|
|
27378
27741
|
IMAGE_HOST = "(?:[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)+(?::[0-9]+)?|localhost(?::[0-9]+)?)";
|
|
@@ -27419,14 +27782,23 @@ var init_dist = __esm({
|
|
|
27419
27782
|
});
|
|
27420
27783
|
|
|
27421
27784
|
// ../../packages/envrun/dist/labels.js
|
|
27785
|
+
function runLabels(runId, callerLabels) {
|
|
27786
|
+
return { ...callerLabels ?? {}, [RUN_ID_LABEL_KEY]: runId, [RUN_LABEL_KEY]: "term-350" };
|
|
27787
|
+
}
|
|
27422
27788
|
function censusTotal(c) {
|
|
27423
27789
|
return c.containers.length + c.volumes.length + c.networks.length;
|
|
27424
27790
|
}
|
|
27425
|
-
function
|
|
27791
|
+
function query(docker3, args) {
|
|
27426
27792
|
const res = docker3.sync([...args], { timeoutMs: 15e3 });
|
|
27427
|
-
if (res.error || res.status !== 0)
|
|
27428
|
-
|
|
27429
|
-
|
|
27793
|
+
if (res.error || res.status !== 0) {
|
|
27794
|
+
const why = res.error?.message ?? (res.stderr.trim() || `exit ${String(res.status)}`);
|
|
27795
|
+
return { ids: [], failure: `docker ${args.slice(0, 2).join(" ")}: ${why}` };
|
|
27796
|
+
}
|
|
27797
|
+
const ids2 = res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
27798
|
+
return { ids: ids2, failure: null };
|
|
27799
|
+
}
|
|
27800
|
+
function ids(docker3, args) {
|
|
27801
|
+
return query(docker3, args).ids;
|
|
27430
27802
|
}
|
|
27431
27803
|
function census(docker3, label) {
|
|
27432
27804
|
const filter = `label=${label}`;
|
|
@@ -27436,30 +27808,73 @@ function census(docker3, label) {
|
|
|
27436
27808
|
networks: ids(docker3, ["network", "ls", "-q", "--filter", filter])
|
|
27437
27809
|
};
|
|
27438
27810
|
}
|
|
27811
|
+
function censusReport(docker3, label) {
|
|
27812
|
+
const filter = `label=${label}`;
|
|
27813
|
+
const failures = [];
|
|
27814
|
+
const ask2 = (args) => {
|
|
27815
|
+
const q = query(docker3, args);
|
|
27816
|
+
if (q.failure !== null)
|
|
27817
|
+
failures.push(q.failure);
|
|
27818
|
+
return q.ids;
|
|
27819
|
+
};
|
|
27820
|
+
const taken = {
|
|
27821
|
+
containers: ask2(["ps", "-aq", "--filter", filter]),
|
|
27822
|
+
volumes: ask2(["volume", "ls", "-q", "--filter", filter]),
|
|
27823
|
+
networks: ask2(["network", "ls", "-q", "--filter", filter])
|
|
27824
|
+
};
|
|
27825
|
+
return failures.length === 0 ? { observed: true, census: taken, unobservedReason: null } : { observed: false, census: taken, unobservedReason: failures.join("; ") };
|
|
27826
|
+
}
|
|
27439
27827
|
function localCensus(label) {
|
|
27440
27828
|
return census(localDockerClient(), label);
|
|
27441
27829
|
}
|
|
27442
|
-
function judgeLeaks(peak, after) {
|
|
27830
|
+
function judgeLeaks(peak, after, observation) {
|
|
27443
27831
|
const labelObserved = peak.containers.length > 0;
|
|
27832
|
+
if (!observation.observed) {
|
|
27833
|
+
return {
|
|
27834
|
+
labelObserved,
|
|
27835
|
+
reaped: false,
|
|
27836
|
+
observed: false,
|
|
27837
|
+
clean: false,
|
|
27838
|
+
state: "unobserved",
|
|
27839
|
+
peak,
|
|
27840
|
+
after,
|
|
27841
|
+
note: `UNOBSERVED, not clean: we could not look at what survived teardown (${observation.unobservedReason ?? "no reason given"}), so nothing is known about leaks on this run, in either direction.`
|
|
27842
|
+
};
|
|
27843
|
+
}
|
|
27444
27844
|
const reaped = censusTotal(after) === 0;
|
|
27445
27845
|
let note;
|
|
27846
|
+
let state;
|
|
27446
27847
|
if (!labelObserved && reaped) {
|
|
27848
|
+
state = "inconclusive";
|
|
27447
27849
|
note = "INCONCLUSIVE, not clean: nothing labelled was ever seen alive, so an empty final census is equally consistent with the label never being applied. The control failed, so the denial proves nothing.";
|
|
27448
27850
|
} else if (!labelObserved) {
|
|
27851
|
+
state = "leak";
|
|
27449
27852
|
note = "no labelled container was observed alive AND objects remain \u2014 the label wiring is wrong.";
|
|
27450
27853
|
} else if (!reaped) {
|
|
27854
|
+
state = "leak";
|
|
27451
27855
|
note = `LEAK: ${String(censusTotal(after))} labelled object(s) survived teardown (containers=${String(after.containers.length)} volumes=${String(after.volumes.length)} networks=${String(after.networks.length)}).`;
|
|
27452
27856
|
} else {
|
|
27857
|
+
state = "clean";
|
|
27453
27858
|
note = `clean: peak ${String(peak.containers.length)} labelled container(s) observed alive, 0 labelled objects remain after teardown.`;
|
|
27454
27859
|
}
|
|
27455
|
-
return {
|
|
27860
|
+
return {
|
|
27861
|
+
labelObserved,
|
|
27862
|
+
reaped,
|
|
27863
|
+
observed: true,
|
|
27864
|
+
clean: labelObserved && reaped,
|
|
27865
|
+
state,
|
|
27866
|
+
peak,
|
|
27867
|
+
after,
|
|
27868
|
+
note
|
|
27869
|
+
};
|
|
27456
27870
|
}
|
|
27457
|
-
var RUN_LABEL_KEY, LabelWatch;
|
|
27871
|
+
var RUN_LABEL_KEY, RUN_ID_LABEL_KEY, LabelWatch, LEAK_STATES;
|
|
27458
27872
|
var init_labels = __esm({
|
|
27459
27873
|
"../../packages/envrun/dist/labels.js"() {
|
|
27460
27874
|
"use strict";
|
|
27461
27875
|
init_dist();
|
|
27462
27876
|
RUN_LABEL_KEY = "supergoal.run";
|
|
27877
|
+
RUN_ID_LABEL_KEY = "supergoal.run-id";
|
|
27463
27878
|
LabelWatch = class {
|
|
27464
27879
|
label;
|
|
27465
27880
|
docker;
|
|
@@ -27509,6 +27924,7 @@ var init_labels = __esm({
|
|
|
27509
27924
|
return this.#samples;
|
|
27510
27925
|
}
|
|
27511
27926
|
};
|
|
27927
|
+
LEAK_STATES = ["clean", "leak", "inconclusive", "unobserved"];
|
|
27512
27928
|
}
|
|
27513
27929
|
});
|
|
27514
27930
|
|
|
@@ -27809,6 +28225,7 @@ http
|
|
|
27809
28225
|
});
|
|
27810
28226
|
|
|
27811
28227
|
// ../../packages/envrun/dist/venue.js
|
|
28228
|
+
import { randomBytes as randomBytes8 } from "crypto";
|
|
27812
28229
|
import { join as join22 } from "path";
|
|
27813
28230
|
function localJailPaths(scratchRoot) {
|
|
27814
28231
|
return {
|
|
@@ -27904,11 +28321,62 @@ function coerceToString(thrown) {
|
|
|
27904
28321
|
return UNCOERCIBLE_THROWN;
|
|
27905
28322
|
}
|
|
27906
28323
|
}
|
|
28324
|
+
function localTreeOwner(ids2 = hostIds()) {
|
|
28325
|
+
if (ids2?.uid !== 0)
|
|
28326
|
+
return void 0;
|
|
28327
|
+
return localGuestUser(ids2) ?? void 0;
|
|
28328
|
+
}
|
|
28329
|
+
function handLocalTreeToGuest(local, owner, chown = chownTree) {
|
|
28330
|
+
if (owner === void 0)
|
|
28331
|
+
return;
|
|
28332
|
+
chown(local.cloneDir, owner);
|
|
28333
|
+
chown(local.scratchRoot, owner);
|
|
28334
|
+
}
|
|
28335
|
+
function localCloneVolumeName(runId, suffix = randomBytes8(4).toString("hex")) {
|
|
28336
|
+
const tail2 = `-${suffix}`;
|
|
28337
|
+
const safe = runId.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
28338
|
+
const head = `th-clone-${safe}`.slice(0, VOLUME_NAME_MAX2 - tail2.length);
|
|
28339
|
+
return validateVolumeName(`${head}${tail2}`, "the local clone volume");
|
|
28340
|
+
}
|
|
28341
|
+
function localCloneFillArgv(from, volume, owner) {
|
|
28342
|
+
const chown = owner === null ? "" : ` && chown -R ${String(owner.uid)}:${String(owner.gid)} /dst`;
|
|
28343
|
+
return [
|
|
28344
|
+
"run",
|
|
28345
|
+
"--rm",
|
|
28346
|
+
"--network=none",
|
|
28347
|
+
`--volume=${from}:/src:ro`,
|
|
28348
|
+
`--volume=${validateVolumeName(volume, "the local clone volume")}:/dst:rw`,
|
|
28349
|
+
"--",
|
|
28350
|
+
STAGE_HELPER_IMAGE,
|
|
28351
|
+
"sh",
|
|
28352
|
+
"-c",
|
|
28353
|
+
`cp -a /src/. /dst/${chown}`
|
|
28354
|
+
];
|
|
28355
|
+
}
|
|
27907
28356
|
function acquireLocalLease(runId) {
|
|
27908
28357
|
const docker3 = localDockerClient();
|
|
27909
28358
|
const containment = selectContainment([containerContainmentOn(docker3)]);
|
|
27910
28359
|
let released = false;
|
|
27911
28360
|
const stagedProxies = [];
|
|
28361
|
+
const owner = localTreeOwner();
|
|
28362
|
+
let cloneVolume;
|
|
28363
|
+
const createdVolumes = [];
|
|
28364
|
+
const fillCloneVolume = (cloneDir) => {
|
|
28365
|
+
const name = localCloneVolumeName(runId);
|
|
28366
|
+
const created = docker3.sync(["volume", "create", `--label=${STAGE_VOLUME_LABEL_KEY}=${runId}`, "--", name], { timeoutMs: LOCAL_VOLUME_CREATE_TIMEOUT_MS });
|
|
28367
|
+
if (created.error || created.status !== 0) {
|
|
28368
|
+
throw new ContainmentRefusalError(`could not create the clone volume ${name} on the local daemon: ${(created.error?.message ?? created.stderr).trim().slice(0, 300)}`);
|
|
28369
|
+
}
|
|
28370
|
+
createdVolumes.push(name);
|
|
28371
|
+
const from = resolverFor("local")(cloneDir, "clone");
|
|
28372
|
+
const filled = docker3.sync(localCloneFillArgv(from, name, localGuestUser()), {
|
|
28373
|
+
timeoutMs: LOCAL_CLONE_FILL_TIMEOUT_MS
|
|
28374
|
+
});
|
|
28375
|
+
if (filled.error || filled.status !== 0) {
|
|
28376
|
+
throw new ContainmentRefusalError(`could not copy the clone into its volume ${name}: ${(filled.error?.message ?? filled.stderr).trim().slice(0, 300)}`);
|
|
28377
|
+
}
|
|
28378
|
+
return name;
|
|
28379
|
+
};
|
|
27912
28380
|
const lease = {
|
|
27913
28381
|
kind: "local",
|
|
27914
28382
|
runId,
|
|
@@ -27918,28 +28386,32 @@ function acquireLocalLease(runId) {
|
|
|
27918
28386
|
// the paths are on this machine AND `canonical()` is what should resolve
|
|
27919
28387
|
// them, which is the behaviour every local run has always had.
|
|
27920
28388
|
pathDomain: "local",
|
|
28389
|
+
// Spread rather than `guestUser: owner`, so a non-root lease has no such key
|
|
28390
|
+
// at all — the same object every local run has always had.
|
|
28391
|
+
...owner === void 0 ? {} : { guestUser: owner },
|
|
27921
28392
|
get released() {
|
|
27922
28393
|
return released;
|
|
27923
28394
|
},
|
|
27924
|
-
|
|
27925
|
-
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
|
|
27929
|
-
|
|
27930
|
-
|
|
27931
|
-
|
|
27932
|
-
|
|
27933
|
-
|
|
27934
|
-
|
|
27935
|
-
|
|
27936
|
-
|
|
28395
|
+
get cloneVolume() {
|
|
28396
|
+
return cloneVolume;
|
|
28397
|
+
},
|
|
28398
|
+
stage: (local) => {
|
|
28399
|
+
if (released) {
|
|
28400
|
+
return Promise.reject(new LeaseReleasedError("this lease was already released, so a clone volume made now would never be removed."));
|
|
28401
|
+
}
|
|
28402
|
+
try {
|
|
28403
|
+
handLocalTreeToGuest(local, owner);
|
|
28404
|
+
cloneVolume = fillCloneVolume(local.cloneDir);
|
|
28405
|
+
} catch (err) {
|
|
28406
|
+
return Promise.reject(err instanceof Error ? err : new Error(String(err)));
|
|
28407
|
+
}
|
|
28408
|
+
return Promise.resolve({
|
|
27937
28409
|
cloneDir: local.cloneDir,
|
|
27938
28410
|
scratchRoot: local.scratchRoot,
|
|
27939
28411
|
previewDir: local.previewDir,
|
|
27940
28412
|
...localJailPaths(local.scratchRoot)
|
|
27941
|
-
})
|
|
27942
|
-
|
|
28413
|
+
});
|
|
28414
|
+
},
|
|
27943
28415
|
// The containment function verbatim, which is the point of the barrel
|
|
27944
28416
|
// re-export rather than a copy here: this venue's daemon and this process
|
|
27945
28417
|
// share a filesystem, so the directory it makes under `tmpdir()` is already
|
|
@@ -27993,7 +28465,7 @@ function acquireLocalLease(runId) {
|
|
|
27993
28465
|
observed: false,
|
|
27994
28466
|
census: { containers: [], volumes: [], networks: [] },
|
|
27995
28467
|
unobservedReason: RELEASED_LEASE_CENSUS_REASON
|
|
27996
|
-
} :
|
|
28468
|
+
} : censusReport(docker3, label)),
|
|
27997
28469
|
publishPreview: (req) => startPreview({ ...req, docker: docker3 }),
|
|
27998
28470
|
release: () => {
|
|
27999
28471
|
if (released) {
|
|
@@ -28009,6 +28481,25 @@ function acquireLocalLease(runId) {
|
|
|
28009
28481
|
const staged = stagedProxies.splice(0, stagedProxies.length);
|
|
28010
28482
|
for (const s of staged)
|
|
28011
28483
|
s.cleanup();
|
|
28484
|
+
const volumeFailures = [];
|
|
28485
|
+
for (const name of createdVolumes.splice(0, createdVolumes.length)) {
|
|
28486
|
+
const removed = docker3.sync(["volume", "rm", "-f", "--", name], {
|
|
28487
|
+
timeoutMs: LOCAL_VOLUME_CREATE_TIMEOUT_MS
|
|
28488
|
+
});
|
|
28489
|
+
if (removed.error || removed.status !== 0) {
|
|
28490
|
+
volumeFailures.push(`${name}: ${(removed.error?.message ?? removed.stderr).trim().slice(0, 200)}`);
|
|
28491
|
+
}
|
|
28492
|
+
}
|
|
28493
|
+
cloneVolume = void 0;
|
|
28494
|
+
if (volumeFailures.length > 0) {
|
|
28495
|
+
return Promise.resolve({
|
|
28496
|
+
kind: "local",
|
|
28497
|
+
released: true,
|
|
28498
|
+
alreadyReleased: false,
|
|
28499
|
+
error: `could not remove the clone volume: ${volumeFailures.join("; ")}`,
|
|
28500
|
+
detail: `the lease is closed, but a clone volume remains (label ${STAGE_VOLUME_LABEL_KEY})`
|
|
28501
|
+
});
|
|
28502
|
+
}
|
|
28012
28503
|
return Promise.resolve({
|
|
28013
28504
|
kind: "local",
|
|
28014
28505
|
released: true,
|
|
@@ -28020,7 +28511,7 @@ function acquireLocalLease(runId) {
|
|
|
28020
28511
|
};
|
|
28021
28512
|
return lease;
|
|
28022
28513
|
}
|
|
28023
|
-
var VenueRollbackError, UNREADABLE_MESSAGE, UNREADABLE_NAME, UNCOERCIBLE_THROWN, UNDESCRIBABLE_THROWN, UNLISTABLE_ROLLBACK_FAILURES, RELEASED_LEASE_CENSUS_REASON, LeaseReleasedError;
|
|
28514
|
+
var VenueRollbackError, UNREADABLE_MESSAGE, UNREADABLE_NAME, UNCOERCIBLE_THROWN, UNDESCRIBABLE_THROWN, UNLISTABLE_ROLLBACK_FAILURES, RELEASED_LEASE_CENSUS_REASON, LeaseReleasedError, STAGE_HELPER_IMAGE, STAGE_VOLUME_LABEL_KEY, VOLUME_NAME_MAX2, LOCAL_VOLUME_CREATE_TIMEOUT_MS, LOCAL_CLONE_FILL_TIMEOUT_MS;
|
|
28024
28515
|
var init_venue = __esm({
|
|
28025
28516
|
"../../packages/envrun/dist/venue.js"() {
|
|
28026
28517
|
"use strict";
|
|
@@ -28045,6 +28536,11 @@ var init_venue = __esm({
|
|
|
28045
28536
|
LeaseReleasedError = class extends Error {
|
|
28046
28537
|
name = "LeaseReleasedError";
|
|
28047
28538
|
};
|
|
28539
|
+
STAGE_HELPER_IMAGE = "busybox:1.37.0";
|
|
28540
|
+
STAGE_VOLUME_LABEL_KEY = "terminalhire.stage";
|
|
28541
|
+
VOLUME_NAME_MAX2 = 128;
|
|
28542
|
+
LOCAL_VOLUME_CREATE_TIMEOUT_MS = 3e4;
|
|
28543
|
+
LOCAL_CLONE_FILL_TIMEOUT_MS = 6e5;
|
|
28048
28544
|
}
|
|
28049
28545
|
});
|
|
28050
28546
|
|
|
@@ -28113,6 +28609,17 @@ caused by: ${frame}`;
|
|
|
28113
28609
|
}
|
|
28114
28610
|
return frames === 0 ? null : out;
|
|
28115
28611
|
}
|
|
28612
|
+
function imageVariantFor(spec) {
|
|
28613
|
+
if (spec.runtime !== "jvm")
|
|
28614
|
+
return void 0;
|
|
28615
|
+
const callsGradle = /(?:^|[\s;&|(])gradle(?=\s|$)/;
|
|
28616
|
+
return [spec.installCommand, spec.testCommand].some((c) => c != null && callsGradle.test(c)) ? "gradle" : void 0;
|
|
28617
|
+
}
|
|
28618
|
+
function shapeFor(runtime, variant) {
|
|
28619
|
+
if (runtime === "jvm" && variant === "gradle")
|
|
28620
|
+
return JVM_GRADLE_IMAGE;
|
|
28621
|
+
return RUNTIME_IMAGES[runtime];
|
|
28622
|
+
}
|
|
28116
28623
|
function atLeast(a, b) {
|
|
28117
28624
|
const left = a.split(".").map(Number);
|
|
28118
28625
|
const right = b.split(".").map(Number);
|
|
@@ -28125,19 +28632,19 @@ function atLeast(a, b) {
|
|
|
28125
28632
|
}
|
|
28126
28633
|
return true;
|
|
28127
28634
|
}
|
|
28128
|
-
function imageForRuntime(runtime, override, version) {
|
|
28635
|
+
function imageForRuntime(runtime, override, version, variant) {
|
|
28129
28636
|
if (override)
|
|
28130
28637
|
return override;
|
|
28131
|
-
const shape =
|
|
28638
|
+
const shape = shapeFor(runtime, variant);
|
|
28132
28639
|
if (!shape) {
|
|
28133
28640
|
throw new RunRefusalError(`no container image is mapped for runtime ${JSON.stringify(runtime)}. Refusing to run it in the Node image: a bare "command not found" exits 127, which classifyVerification already owns as ours \u2014 but a test script that RUNS and fails inside on the missing tool exits with its own status and prints to stdout, and that falls through to tests-failed \u2014 a false red blamed on the repo.`);
|
|
28134
28641
|
}
|
|
28135
|
-
if (version === void 0 || version === null)
|
|
28642
|
+
if (version === void 0 || version === null || shape.fixed)
|
|
28136
28643
|
return unversionedImage(shape);
|
|
28137
28644
|
if (!TAG_VERSION.test(version)) {
|
|
28138
28645
|
throw new RunRefusalError(`runtime version ${JSON.stringify(version)} is not a bare version, so no image tag can be built from it. Refusing rather than booting the default: the repo asked for a version, and supplying a different one silently is what TERM-643 fixed.`);
|
|
28139
28646
|
}
|
|
28140
|
-
return `${shape.repository}:${version}${shape.suffix}`;
|
|
28647
|
+
return `${shape.repository}:${shape.prefix ?? ""}${version}${shape.suffix}`;
|
|
28141
28648
|
}
|
|
28142
28649
|
function setManifestProbe(probe) {
|
|
28143
28650
|
const previous = manifestProbe;
|
|
@@ -28150,39 +28657,47 @@ function imageDefinitelyAbsent(image) {
|
|
|
28150
28657
|
return false;
|
|
28151
28658
|
return /manifest unknown|no such manifest/i.test(res.output);
|
|
28152
28659
|
}
|
|
28153
|
-
function resolvePublishedImage(image, runtime, version) {
|
|
28660
|
+
function resolvePublishedImage(image, runtime, version, variant) {
|
|
28154
28661
|
if (!imageDefinitelyAbsent(image))
|
|
28155
28662
|
return image;
|
|
28156
|
-
const shape =
|
|
28663
|
+
const shape = shapeFor(runtime, variant);
|
|
28157
28664
|
if (shape && shape.declaredIsFloor && atLeast(shape.defaultVersion, version)) {
|
|
28158
28665
|
return unversionedImage(shape);
|
|
28159
28666
|
}
|
|
28160
28667
|
throw new RunRefusalError(`the repo declares ${runtime} ${version}, and no image is published at ${image}` + (shape && shape.declaredIsFloor ? `. Our default is ${shape.defaultVersion}, which is OLDER than that, so falling back would run the repo under a toolchain it says it cannot use` : `. ${runtime} treats a declared version as an exact pin, not a minimum, so a different one is a different environment`) + ". Refusing rather than booting a version the repo did not ask for \u2014 that substitution is what made this class of failure unattributable (TERM-643). Pass an explicit image to override.");
|
|
28161
28668
|
}
|
|
28162
28669
|
function resolveImageForSpec(spec, override) {
|
|
28163
|
-
const
|
|
28164
|
-
const
|
|
28670
|
+
const variant = imageVariantFor(spec);
|
|
28671
|
+
const declared = shapeFor(spec.runtime, variant)?.fixed ? null : spec.runtimeVersion;
|
|
28672
|
+
const image = imageForRuntime(spec.runtime, override, declared, variant);
|
|
28165
28673
|
const constructed = (() => {
|
|
28166
28674
|
if (declared === null)
|
|
28167
28675
|
return null;
|
|
28168
28676
|
try {
|
|
28169
|
-
return imageForRuntime(spec.runtime, void 0, declared);
|
|
28677
|
+
return imageForRuntime(spec.runtime, void 0, declared, variant);
|
|
28170
28678
|
} catch {
|
|
28171
28679
|
return null;
|
|
28172
28680
|
}
|
|
28173
28681
|
})();
|
|
28174
28682
|
if (constructed !== null && image === constructed && declared !== null) {
|
|
28175
|
-
return resolvePublishedImage(image, spec.runtime, declared);
|
|
28683
|
+
return resolvePublishedImage(image, spec.runtime, declared, variant);
|
|
28176
28684
|
}
|
|
28177
28685
|
return image;
|
|
28178
28686
|
}
|
|
28179
28687
|
function installEnvironmentFailureNote(install, image) {
|
|
28180
|
-
const base = `the install step exited ${String(install.exitCode)}, so the test command was never invoked. The repo has not been judged; this is an environment failure
|
|
28688
|
+
const base = `the install step exited ${String(install.exitCode)}, so the test command was never invoked. The repo has not been judged; this is an environment failure.` + refusedHostsSentence(install.egressDenied ?? []);
|
|
28181
28689
|
if (!MISSING_IMAGE_SHAPE.test(`${install.stdout}
|
|
28182
28690
|
${install.stderr}`))
|
|
28183
28691
|
return base;
|
|
28184
28692
|
return `${base} The container image ${image} is not present on this machine \u2014 run \`docker pull ${image}\` and try again.`;
|
|
28185
28693
|
}
|
|
28694
|
+
function refusedHostsSentence(hosts) {
|
|
28695
|
+
if (hosts.length === 0)
|
|
28696
|
+
return "";
|
|
28697
|
+
const named = hosts.slice(0, MAX_NAMED_REFUSED_HOSTS).join(", ");
|
|
28698
|
+
const rest = hosts.length - MAX_NAMED_REFUSED_HOSTS;
|
|
28699
|
+
return ` During install the network proxy refused ${named}` + (rest > 0 ? ` and ${String(rest)} more` : "") + ". Install reaches only the hosts on its allowlist, so a dependency whose install script downloads from any other host cannot get what it asked for.";
|
|
28700
|
+
}
|
|
28186
28701
|
function classifySingleRun(run2) {
|
|
28187
28702
|
return classifyVerification(run2).outcome;
|
|
28188
28703
|
}
|
|
@@ -28199,12 +28714,22 @@ function assertVenueOwnerDeclared(lease) {
|
|
|
28199
28714
|
return;
|
|
28200
28715
|
throw new RunRefusalError("this venue did not say which account owns the tree it staged, so the guest would run under this machine's uid and could not write its own clone. We refuse rather than report that permission error as the repo's tests failing.");
|
|
28201
28716
|
}
|
|
28717
|
+
function refuseUnbuildableSpec(spec) {
|
|
28718
|
+
if (spec.runtime !== "jvm" || spec.installCommand !== null || spec.testCommand !== null)
|
|
28719
|
+
return;
|
|
28720
|
+
const why = spec.unresolved.find((r) => r.kind === "no-test-command")?.detail;
|
|
28721
|
+
if (why === void 0) {
|
|
28722
|
+
throw new EnvRunError("internal: a jvm spec with no test command carries no no-test-command reason");
|
|
28723
|
+
}
|
|
28724
|
+
throw new RunRefusalError(`refusing to run this repository: ${why}.`);
|
|
28725
|
+
}
|
|
28202
28726
|
async function runEnvironmentSpec(req) {
|
|
28203
28727
|
const startedAt = Date.now();
|
|
28204
28728
|
const containment = req.lease.containment;
|
|
28205
28729
|
if (containment.kind !== "container") {
|
|
28206
28730
|
throw new EnvRunError(`phase 2 requires the container tier, got ${containment.kind}. Refusing: a container phase that silently ran under seatbelt would make every container claim vacuous.`);
|
|
28207
28731
|
}
|
|
28732
|
+
refuseUnbuildableSpec(req.spec);
|
|
28208
28733
|
assertVenueOwnerDeclared(req.lease);
|
|
28209
28734
|
const image = resolveImageForSpec(req.spec, req.image);
|
|
28210
28735
|
const { jail, tmp } = req;
|
|
@@ -28234,6 +28759,8 @@ async function runEnvironmentSpec(req) {
|
|
|
28234
28759
|
// TERM-913: the third answer about that machine — which volumes the
|
|
28235
28760
|
// fence mounts in place of the noexec stage. Undefined on a local lease.
|
|
28236
28761
|
stageVolumes: req.lease.stageVolumes,
|
|
28762
|
+
// TERM-1106: the local venue's copy of the clone. Undefined on a hosted lease.
|
|
28763
|
+
cloneVolume: req.lease.cloneVolume,
|
|
28237
28764
|
env,
|
|
28238
28765
|
image,
|
|
28239
28766
|
labels,
|
|
@@ -28247,9 +28774,10 @@ async function runEnvironmentSpec(req) {
|
|
|
28247
28774
|
installOk: false
|
|
28248
28775
|
};
|
|
28249
28776
|
} else if (req.spec.testCommand === null) {
|
|
28777
|
+
const why = req.spec.unresolved.find((r) => r.kind === "no-test-command")?.detail;
|
|
28250
28778
|
result = {
|
|
28251
28779
|
outcome: "no-tests-observed",
|
|
28252
|
-
note: "the spec derived no test command, so nothing was executed: nothing failed and nothing ran. Certain, not inferred \u2014 no command was ever invoked.",
|
|
28780
|
+
note: "the spec derived no test command, so nothing was executed: nothing failed and nothing ran. Certain, not inferred \u2014 no command was ever invoked." + (why === void 0 ? "" : ` Why: ${why}.`),
|
|
28253
28781
|
installOk: true
|
|
28254
28782
|
};
|
|
28255
28783
|
} else {
|
|
@@ -28267,12 +28795,14 @@ async function runEnvironmentSpec(req) {
|
|
|
28267
28795
|
// TERM-913: the third answer about that machine — which volumes the
|
|
28268
28796
|
// fence mounts in place of the noexec stage. Undefined on a local lease.
|
|
28269
28797
|
stageVolumes: req.lease.stageVolumes,
|
|
28798
|
+
// TERM-1106: the local venue's copy of the clone. Undefined on a hosted lease.
|
|
28799
|
+
cloneVolume: req.lease.cloneVolume,
|
|
28270
28800
|
env,
|
|
28271
28801
|
image,
|
|
28272
28802
|
labels,
|
|
28273
28803
|
timeoutMs: req.testTimeoutMs ?? 9e5
|
|
28274
28804
|
});
|
|
28275
|
-
const verdict = classifyVerification(toExecution(test));
|
|
28805
|
+
const verdict = classifyVerification({ ...toExecution(test), runtime: req.spec.runtime });
|
|
28276
28806
|
result = { outcome: verdict.outcome, note: verdict.reason, installOk: true };
|
|
28277
28807
|
}
|
|
28278
28808
|
} finally {
|
|
@@ -28286,6 +28816,10 @@ async function runEnvironmentSpec(req) {
|
|
|
28286
28816
|
volumes: [],
|
|
28287
28817
|
networks: []
|
|
28288
28818
|
};
|
|
28819
|
+
const observation = afterReport ?? {
|
|
28820
|
+
observed: false,
|
|
28821
|
+
unobservedReason: "the run carried no label, so there was nothing to count by"
|
|
28822
|
+
};
|
|
28289
28823
|
return {
|
|
28290
28824
|
outcome: result.outcome,
|
|
28291
28825
|
tier: "container",
|
|
@@ -28294,7 +28828,7 @@ async function runEnvironmentSpec(req) {
|
|
|
28294
28828
|
test,
|
|
28295
28829
|
installOk: result.installOk,
|
|
28296
28830
|
counts: test ? readCounts(test.stdout, test.stderr) : null,
|
|
28297
|
-
leaks: judgeLeaks(peak, after),
|
|
28831
|
+
leaks: judgeLeaks(peak, after, observation),
|
|
28298
28832
|
note: result.note,
|
|
28299
28833
|
wallMs: Date.now() - startedAt
|
|
28300
28834
|
};
|
|
@@ -28322,6 +28856,7 @@ async function runStep(containment, r) {
|
|
|
28322
28856
|
pathDomain: r.pathDomain,
|
|
28323
28857
|
guestUser: r.guestUser,
|
|
28324
28858
|
stageVolumes: r.stageVolumes,
|
|
28859
|
+
cloneVolume: r.cloneVolume,
|
|
28325
28860
|
program: "/bin/sh",
|
|
28326
28861
|
args: ["-c", withUserScriptPath(r.command)]
|
|
28327
28862
|
};
|
|
@@ -28341,10 +28876,11 @@ async function runStep(containment, r) {
|
|
|
28341
28876
|
stdout: res.stdout,
|
|
28342
28877
|
stderr: res.stderr,
|
|
28343
28878
|
timedOut: res.timedOut,
|
|
28344
|
-
wallMs: Date.now() - startedAt
|
|
28879
|
+
wallMs: Date.now() - startedAt,
|
|
28880
|
+
...res.egressDenied && res.egressDenied.length > 0 ? { egressDenied: [...new Set(res.egressDenied)] } : {}
|
|
28345
28881
|
};
|
|
28346
28882
|
}
|
|
28347
|
-
var EnvRunError, RunRefusalError, MAX_CAUSE_FRAMES, CHAIN_UNREADABLE, CHAIN_TOO_DEEP, RUNTIME_IMAGES, unversionedImage, TAG_VERSION, dockerManifestProbe, manifestProbe, MISSING_IMAGE_SHAPE;
|
|
28883
|
+
var EnvRunError, RunRefusalError, MAX_CAUSE_FRAMES, CHAIN_UNREADABLE, CHAIN_TOO_DEEP, RUNTIME_IMAGES, JVM_GRADLE_IMAGE, unversionedImage, TAG_VERSION, dockerManifestProbe, manifestProbe, MISSING_IMAGE_SHAPE, MAX_NAMED_REFUSED_HOSTS;
|
|
28348
28884
|
var init_execute = __esm({
|
|
28349
28885
|
"../../packages/envrun/dist/execute.js"() {
|
|
28350
28886
|
"use strict";
|
|
@@ -28411,9 +28947,56 @@ var init_execute = __esm({
|
|
|
28411
28947
|
suffix: "",
|
|
28412
28948
|
defaultVersion: "8.0",
|
|
28413
28949
|
declaredIsFloor: true
|
|
28950
|
+
},
|
|
28951
|
+
/**
|
|
28952
|
+
* TERM-1122. Maven's image, and the one a Gradle WRAPPER repo gets too: the wrapper
|
|
28953
|
+
* downloads its own Gradle, so it needs a JDK and nothing else. MEASURED
|
|
28954
|
+
* 2026-09-18: `maven:3.9-eclipse-temurin-21` carries `git`, `mvn` and JDK 21 on
|
|
28955
|
+
* Ubuntu 24.04, which is why this is not `eclipse-temurin` (no `git`, rejected
|
|
28956
|
+
* under TERM-1139). The JDK sits AFTER maven's own version in the tag, hence
|
|
28957
|
+
* `prefix`, and the major is all envspec derives (`javaMajor`).
|
|
28958
|
+
*
|
|
28959
|
+
* `declaredIsFloor`: a JDK builds for older `--release` targets, so a repo asking
|
|
28960
|
+
* for 16, which has no image, runs on 21. A Gradle toolchain is stricter than a
|
|
28961
|
+
* floor and asks for its exact JDK; Gradle's refusal on the wrong one reads as
|
|
28962
|
+
* ours (`classify.ts`, `isOfflineBuildGap`), never as the repo's.
|
|
28963
|
+
*/
|
|
28964
|
+
jvm: {
|
|
28965
|
+
repository: "maven",
|
|
28966
|
+
prefix: "3.9-eclipse-temurin-",
|
|
28967
|
+
suffix: "",
|
|
28968
|
+
defaultVersion: "21",
|
|
28969
|
+
declaredIsFloor: true
|
|
28970
|
+
},
|
|
28971
|
+
/**
|
|
28972
|
+
* TERM-1123. OUR image (`packages/envrun/images/cpp/Dockerfile`): Debian bookworm
|
|
28973
|
+
* with the C/C++ toolchain, CMake, Meson and the common -dev libraries baked in.
|
|
28974
|
+
* No official image carries that set. Libraries are baked rather than installed per
|
|
28975
|
+
* run because the install step has network only through the allowlist, and apt is
|
|
28976
|
+
* not on it; a repo needing a library the image lacks gets `test-command-unavailable`
|
|
28977
|
+
* (`isMissingSystemDependency`), never a red.
|
|
28978
|
+
*
|
|
28979
|
+
* `fixed`: a C/C++ repo declares no compiler version envspec can read, so there is
|
|
28980
|
+
* nothing to derive and nothing to probe. The tag moves only when the Dockerfile
|
|
28981
|
+
* does. It lives in a PUBLIC repository, apart from the private venue images, so a
|
|
28982
|
+
* developer's own Docker can pull it with no credential.
|
|
28983
|
+
*/
|
|
28984
|
+
cpp: {
|
|
28985
|
+
repository: "us-east1-docker.pkg.dev/terminalhire-pool/runtime-images/cpp",
|
|
28986
|
+
suffix: "",
|
|
28987
|
+
defaultVersion: "1",
|
|
28988
|
+
declaredIsFloor: false,
|
|
28989
|
+
fixed: true
|
|
28414
28990
|
}
|
|
28415
28991
|
};
|
|
28416
|
-
|
|
28992
|
+
JVM_GRADLE_IMAGE = {
|
|
28993
|
+
repository: "gradle",
|
|
28994
|
+
prefix: "jdk",
|
|
28995
|
+
suffix: "",
|
|
28996
|
+
defaultVersion: "21",
|
|
28997
|
+
declaredIsFloor: true
|
|
28998
|
+
};
|
|
28999
|
+
unversionedImage = (shape) => `${shape.repository}:${shape.prefix ?? ""}${shape.defaultVersion}${shape.suffix}`;
|
|
28417
29000
|
TAG_VERSION = /^\d+(?:\.\d+){0,2}$/;
|
|
28418
29001
|
dockerManifestProbe = (image) => {
|
|
28419
29002
|
const res = spawnSync4("docker", ["manifest", "inspect", image], {
|
|
@@ -28424,6 +29007,7 @@ var init_execute = __esm({
|
|
|
28424
29007
|
};
|
|
28425
29008
|
manifestProbe = dockerManifestProbe;
|
|
28426
29009
|
MISSING_IMAGE_SHAPE = /Unable to find image ['"][^'"]*['"] locally/i;
|
|
29010
|
+
MAX_NAMED_REFUSED_HOSTS = 5;
|
|
28427
29011
|
}
|
|
28428
29012
|
});
|
|
28429
29013
|
|
|
@@ -28498,7 +29082,7 @@ var init_dsse = __esm({
|
|
|
28498
29082
|
});
|
|
28499
29083
|
|
|
28500
29084
|
// ../../packages/attest/dist/sealedbox.js
|
|
28501
|
-
import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, diffieHellman, generateKeyPairSync as generateKeyPairSync2, hkdfSync as hkdfSync2, randomBytes as
|
|
29085
|
+
import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, diffieHellman, generateKeyPairSync as generateKeyPairSync2, hkdfSync as hkdfSync2, randomBytes as randomBytes9 } from "crypto";
|
|
28502
29086
|
var init_sealedbox = __esm({
|
|
28503
29087
|
"../../packages/attest/dist/sealedbox.js"() {
|
|
28504
29088
|
"use strict";
|
|
@@ -28508,7 +29092,7 @@ var init_sealedbox = __esm({
|
|
|
28508
29092
|
});
|
|
28509
29093
|
|
|
28510
29094
|
// ../../packages/attest/dist/aead.js
|
|
28511
|
-
import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as
|
|
29095
|
+
import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes10 } from "crypto";
|
|
28512
29096
|
var init_aead = __esm({
|
|
28513
29097
|
"../../packages/attest/dist/aead.js"() {
|
|
28514
29098
|
"use strict";
|
|
@@ -29105,7 +29689,14 @@ function answerDidItPass(r) {
|
|
|
29105
29689
|
const passed = r.status === "verified" && r.outcome !== null && isGreen(r.outcome);
|
|
29106
29690
|
return { passed, summary: renderVerdictLine(r), lookAt: r.preview?.url ?? null };
|
|
29107
29691
|
}
|
|
29108
|
-
|
|
29692
|
+
function exitCodeFor(r) {
|
|
29693
|
+
if (r.status === "refused")
|
|
29694
|
+
return 2;
|
|
29695
|
+
if (r.outcome === null)
|
|
29696
|
+
return 2;
|
|
29697
|
+
return OUTCOME_EXIT_CODES[r.outcome];
|
|
29698
|
+
}
|
|
29699
|
+
var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS, OUTCOME_EXIT_CODES;
|
|
29109
29700
|
var init_result = __esm({
|
|
29110
29701
|
"../../packages/envrun/dist/result.js"() {
|
|
29111
29702
|
"use strict";
|
|
@@ -29149,6 +29740,7 @@ var init_result = __esm({
|
|
|
29149
29740
|
"containerImageDigest",
|
|
29150
29741
|
"imageSource",
|
|
29151
29742
|
"leaksClean",
|
|
29743
|
+
"leakState",
|
|
29152
29744
|
"venue"
|
|
29153
29745
|
];
|
|
29154
29746
|
RENDER_NONE = null;
|
|
@@ -29183,17 +29775,44 @@ var init_result = __esm({
|
|
|
29183
29775
|
// Shown only when a human chose the environment. `detected` is the ordinary case and
|
|
29184
29776
|
// saying so on every run would train the reader to skip the line that matters.
|
|
29185
29777
|
imageSource: (r) => r.imageSource === "detected" || r.imageSource === "none" ? null : `image source ${r.imageSource} (not signed)`,
|
|
29186
|
-
|
|
29778
|
+
// Printed through `leakState` below, which knows WHY a false is false. Rendering both would
|
|
29779
|
+
// print the leak warning on a run where we merely could not look (TERM-1144).
|
|
29780
|
+
leaksClean: RENDER_NONE,
|
|
29781
|
+
// Silent when clean and when refused, as the boolean's line was. The two not-a-leak states
|
|
29782
|
+
// get their own sentence, because "we could not check" read as "we found a leak" is the
|
|
29783
|
+
// exact confusion this field exists to end.
|
|
29784
|
+
leakState: (r) => {
|
|
29785
|
+
switch (r.leakState) {
|
|
29786
|
+
case null:
|
|
29787
|
+
case "clean":
|
|
29788
|
+
return null;
|
|
29789
|
+
case "leak":
|
|
29790
|
+
return "WARNING labelled Docker objects survived teardown";
|
|
29791
|
+
case "inconclusive":
|
|
29792
|
+
return "leak check inconclusive \u2014 no labelled container was seen while the run was live, so finding nothing afterwards proves nothing";
|
|
29793
|
+
case "unobserved":
|
|
29794
|
+
return "leak check not taken \u2014 we could not check what was left after teardown";
|
|
29795
|
+
}
|
|
29796
|
+
},
|
|
29187
29797
|
// Absent on most runs, so it prints only when there is something to say. Silence
|
|
29188
29798
|
// here is the honest rendering of "no venue answered": a placeholder line would
|
|
29189
29799
|
// invite a reader to treat an unanswered probe as a described venue.
|
|
29190
29800
|
venue: (r) => r.venue === null ? null : renderVenueLine(r.venue)
|
|
29191
29801
|
};
|
|
29802
|
+
OUTCOME_EXIT_CODES = {
|
|
29803
|
+
completed: 0,
|
|
29804
|
+
"tests-failed": 1,
|
|
29805
|
+
"no-tests-observed": 1,
|
|
29806
|
+
"budget-exceeded": 1,
|
|
29807
|
+
"counts-unparsed": 2,
|
|
29808
|
+
"test-command-unavailable": 2,
|
|
29809
|
+
"environment-exhausted": 2
|
|
29810
|
+
};
|
|
29192
29811
|
}
|
|
29193
29812
|
});
|
|
29194
29813
|
|
|
29195
29814
|
// ../../packages/envrun/dist/attestation.js
|
|
29196
|
-
import { createHash as createHash8, randomBytes as
|
|
29815
|
+
import { createHash as createHash8, randomBytes as randomBytes11 } from "crypto";
|
|
29197
29816
|
function contradicts(outcome, counts, exitCode) {
|
|
29198
29817
|
const budget = OUTCOME_TO_BUDGET[outcome];
|
|
29199
29818
|
if (budget === null)
|
|
@@ -29373,7 +29992,7 @@ function toAcceptancePredicate(pair, opts = {}) {
|
|
|
29373
29992
|
// RepoDigest (`repo@sha256:…`) carries the repo name and the content hash, and the
|
|
29374
29993
|
// tag it drops is the part a registry can re-point (TERM-893).
|
|
29375
29994
|
enclave_measurement: localMeasurement(patched.containerImageDigest),
|
|
29376
|
-
nonce: opts.nonce ??
|
|
29995
|
+
nonce: opts.nonce ?? randomBytes11(16).toString("hex"),
|
|
29377
29996
|
run_policy: { max_attempts: opts.maxAttempts ?? 1, budget_outcome: budget }
|
|
29378
29997
|
}
|
|
29379
29998
|
};
|
|
@@ -30351,12 +30970,12 @@ function socketErrorReason(err) {
|
|
|
30351
30970
|
return err.name;
|
|
30352
30971
|
}
|
|
30353
30972
|
function fetchAttestationOverTls(req) {
|
|
30354
|
-
return new Promise((
|
|
30973
|
+
return new Promise((resolve5) => {
|
|
30355
30974
|
let whole;
|
|
30356
30975
|
const settle = (reading) => {
|
|
30357
30976
|
if (whole !== void 0)
|
|
30358
30977
|
clearTimeout(whole);
|
|
30359
|
-
|
|
30978
|
+
resolve5(reading);
|
|
30360
30979
|
};
|
|
30361
30980
|
let cert;
|
|
30362
30981
|
let key;
|
|
@@ -31499,11 +32118,7 @@ function makeLease(p) {
|
|
|
31499
32118
|
}
|
|
31500
32119
|
try {
|
|
31501
32120
|
check("counting what the run left behind");
|
|
31502
|
-
return Promise.resolve(
|
|
31503
|
-
observed: true,
|
|
31504
|
-
census: census(p.docker, label),
|
|
31505
|
-
unobservedReason: null
|
|
31506
|
-
});
|
|
32121
|
+
return Promise.resolve(censusReport(p.docker, label));
|
|
31507
32122
|
} catch (err) {
|
|
31508
32123
|
return Promise.resolve({
|
|
31509
32124
|
observed: false,
|
|
@@ -31561,7 +32176,7 @@ function makeLease(p) {
|
|
|
31561
32176
|
}
|
|
31562
32177
|
};
|
|
31563
32178
|
}
|
|
31564
|
-
var SSH_READY_BUDGET_MS, SSH_PROBE_INTERVAL_MS, SSH_PROBE_TIMEOUT_MS, TUNNEL_BUDGET_MS, TUNNEL_POLL_INTERVAL_MS, GOOGLE_JWKS_URL, JWKS_FETCH_TIMEOUT_MS, CREDENTIAL_QUERY_PARAM, UNDECODABLE, STAGE_PUSH_TIMEOUT_MS, DISPATCHED_PROBE_TIMEOUT_MS, DISPATCHED_STATUS_ARGV, DISPATCHED_GIT_CANDIDATES, PROXY_CLEANUP_TIMEOUT_MS, OWNER_PROBE_TIMEOUT_MS, BOOT_TIMEOUT_MS, MKDIR_TIMEOUT_MS, DELETE_TIMEOUT_MS, LOCAL_GCLOUD_TIMEOUT_MS, SERVICE_ACCOUNT_ACTIVATE_TIMEOUT_MS, SOCKET_DIR_PREFIX, VENUE_SOCKET_NAME, HostedVenueError, VENUE_GCLOUD_CONFIG, SERVICE_ACCOUNT_SUFFIX, GCLOUD_PRINCIPAL_OVERRIDES, defaultHostedVenueIo, VENUE_SSH_USER, GCE_METADATA_IDENTITY_URL, COMPACT_JWT,
|
|
32179
|
+
var SSH_READY_BUDGET_MS, SSH_PROBE_INTERVAL_MS, SSH_PROBE_TIMEOUT_MS, TUNNEL_BUDGET_MS, TUNNEL_POLL_INTERVAL_MS, GOOGLE_JWKS_URL, JWKS_FETCH_TIMEOUT_MS, CREDENTIAL_QUERY_PARAM, UNDECODABLE, STAGE_PUSH_TIMEOUT_MS, DISPATCHED_PROBE_TIMEOUT_MS, DISPATCHED_STATUS_ARGV, DISPATCHED_GIT_CANDIDATES, PROXY_CLEANUP_TIMEOUT_MS, OWNER_PROBE_TIMEOUT_MS, BOOT_TIMEOUT_MS, MKDIR_TIMEOUT_MS, DELETE_TIMEOUT_MS, LOCAL_GCLOUD_TIMEOUT_MS, SERVICE_ACCOUNT_ACTIVATE_TIMEOUT_MS, SOCKET_DIR_PREFIX, VENUE_SOCKET_NAME, HostedVenueError, VENUE_GCLOUD_CONFIG, SERVICE_ACCOUNT_SUFFIX, GCLOUD_PRINCIPAL_OVERRIDES, defaultHostedVenueIo, VENUE_SSH_USER, GCE_METADATA_IDENTITY_URL, COMPACT_JWT, VOLUME_CREATE_TIMEOUT_MS, EXEC_PROBE_TIMEOUT_MS, POPULATE_TIMEOUT_MS, STAGE_PROOF_PREFIX, IAP_NOT_READY, IAP_BACKEND_UNREACHABLE, IAP_DENIED, TERMINAL_GCP, INSTANCE_NOT_RUNNING, PREEMPTED, HOST_KEY_MISMATCH, SSH_KEY_NOT_READY, DAEMON_NOT_READY, SSH_NOT_ANSWERING, CS_ATTEST_PORT, CS_DOCKER_PORT, CS_READY_BUDGET_MS, CS_ATTEST_TIMEOUT_MS, CS_PULL_TIMEOUT_MS, CS_ATTEST_INTERVAL_MS, OPENSSL_TIMEOUT_MS, CS_RUN_ARGS, CS_GUEST_USER, CS_STAGE_ROOT, CLIENT_CERT_STAND_IN;
|
|
31565
32180
|
var init_hostedVenue = __esm({
|
|
31566
32181
|
"../../packages/envrun/dist/hostedVenue.js"() {
|
|
31567
32182
|
"use strict";
|
|
@@ -31710,18 +32325,18 @@ var init_hostedVenue = __esm({
|
|
|
31710
32325
|
for (let i = 0; i < n; i += 1) {
|
|
31711
32326
|
const server = createServer();
|
|
31712
32327
|
servers.push(server);
|
|
31713
|
-
await new Promise((
|
|
32328
|
+
await new Promise((resolve5, reject) => {
|
|
31714
32329
|
server.once("error", reject);
|
|
31715
32330
|
server.listen(0, "localhost", () => {
|
|
31716
|
-
|
|
32331
|
+
resolve5();
|
|
31717
32332
|
});
|
|
31718
32333
|
});
|
|
31719
32334
|
}
|
|
31720
32335
|
return servers.map((s) => s.address().port);
|
|
31721
32336
|
} finally {
|
|
31722
|
-
await Promise.all(servers.map((s) => new Promise((
|
|
32337
|
+
await Promise.all(servers.map((s) => new Promise((resolve5) => {
|
|
31723
32338
|
s.close(() => {
|
|
31724
|
-
|
|
32339
|
+
resolve5();
|
|
31725
32340
|
});
|
|
31726
32341
|
})));
|
|
31727
32342
|
}
|
|
@@ -31735,11 +32350,9 @@ var init_hostedVenue = __esm({
|
|
|
31735
32350
|
VENUE_SSH_USER = "th-runner";
|
|
31736
32351
|
GCE_METADATA_IDENTITY_URL = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity";
|
|
31737
32352
|
COMPACT_JWT = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
31738
|
-
STAGE_HELPER_IMAGE = "busybox:1.37.0";
|
|
31739
32353
|
VOLUME_CREATE_TIMEOUT_MS = 3e4;
|
|
31740
32354
|
EXEC_PROBE_TIMEOUT_MS = 12e4;
|
|
31741
32355
|
POPULATE_TIMEOUT_MS = STAGE_PUSH_TIMEOUT_MS;
|
|
31742
|
-
STAGE_VOLUME_LABEL_KEY = "terminalhire.stage";
|
|
31743
32356
|
STAGE_PROOF_PREFIX = ".th-proven-";
|
|
31744
32357
|
IAP_NOT_READY = /\b4047\s*[:\]]/;
|
|
31745
32358
|
IAP_BACKEND_UNREACHABLE = /\b4003\s*[:\]]/;
|
|
@@ -31771,7 +32384,7 @@ function localDockerPlacement() {
|
|
|
31771
32384
|
kind: "local-docker",
|
|
31772
32385
|
refusal: null,
|
|
31773
32386
|
venue: () => localVenue(),
|
|
31774
|
-
imageFor: (runtime, override, version) => imageForRuntime(runtime, override, version)
|
|
32387
|
+
imageFor: (runtime, override, version, variant) => imageForRuntime(runtime, override, version, variant)
|
|
31775
32388
|
};
|
|
31776
32389
|
}
|
|
31777
32390
|
function hostedPoolPlacement() {
|
|
@@ -31779,7 +32392,7 @@ function hostedPoolPlacement() {
|
|
|
31779
32392
|
kind: "hosted-pool",
|
|
31780
32393
|
refusal: null,
|
|
31781
32394
|
venue: () => hostedVenue(),
|
|
31782
|
-
imageFor: (runtime, override, version) => imageForRuntime(runtime, override, version)
|
|
32395
|
+
imageFor: (runtime, override, version, variant) => imageForRuntime(runtime, override, version, variant)
|
|
31783
32396
|
};
|
|
31784
32397
|
}
|
|
31785
32398
|
function placementFor(kind) {
|
|
@@ -32344,6 +32957,12 @@ function detectRuntime(repo) {
|
|
|
32344
32957
|
if (repo.listNames("").some((name) => /\.(?:csproj|fsproj|sln)$/.test(name))) {
|
|
32345
32958
|
candidates.push("dotnet");
|
|
32346
32959
|
}
|
|
32960
|
+
if (candidates.length === 0 && repo.listFiles("").some((f) => f.endsWith(".java"))) {
|
|
32961
|
+
candidates.push("jvm");
|
|
32962
|
+
}
|
|
32963
|
+
if (!candidates.includes("cpp") && MAKEFILE_NAMES.some((file) => repo.exists(file)) && repo.listFiles("").some((f) => C_FAMILY_SOURCE.test(f))) {
|
|
32964
|
+
candidates.push("cpp");
|
|
32965
|
+
}
|
|
32347
32966
|
return { runtime: candidates[0] ?? "unknown", candidates };
|
|
32348
32967
|
}
|
|
32349
32968
|
function readJsonObject(repo, path5) {
|
|
@@ -32491,6 +33110,49 @@ function basePythonInstallCommand(repo) {
|
|
|
32491
33110
|
}
|
|
32492
33111
|
return null;
|
|
32493
33112
|
}
|
|
33113
|
+
function rehearse(command) {
|
|
33114
|
+
const copy = '"$R/.terminalhire-rehearsal"';
|
|
33115
|
+
const home = '"$R/.terminalhire-rehearsal-home"';
|
|
33116
|
+
return `{ R="$HOME"; rm -rf ${copy} ${home}; mkdir -p ${home} && cp -R . ${copy} && (cd ${copy} && HOME=${home} GRADLE_USER_HOME="$R/.gradle" JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Duser.home=$R/.terminalhire-rehearsal-home" ${command}); rm -rf ${copy} ${home}; true; }`;
|
|
33117
|
+
}
|
|
33118
|
+
function cmakeCode(text) {
|
|
33119
|
+
return text.replace(/#\[(=*)\[[\s\S]*?\]\1\]/g, "").replace(/#.*$/gm, "");
|
|
33120
|
+
}
|
|
33121
|
+
function makeTargets(text) {
|
|
33122
|
+
const targets = /* @__PURE__ */ new Set();
|
|
33123
|
+
for (const line of text.split("\n")) {
|
|
33124
|
+
if (line.startsWith(" "))
|
|
33125
|
+
continue;
|
|
33126
|
+
const rule = /^([^:=#\t][^:=#]*?)[ \t]*::?(?!:?=)/.exec(line);
|
|
33127
|
+
if (rule)
|
|
33128
|
+
for (const name of rule[1].trim().split(/\s+/))
|
|
33129
|
+
targets.add(name);
|
|
33130
|
+
}
|
|
33131
|
+
return targets;
|
|
33132
|
+
}
|
|
33133
|
+
function cppCommands(repo) {
|
|
33134
|
+
const cmake = repo.readText("CMakeLists.txt");
|
|
33135
|
+
if (cmake !== null) {
|
|
33136
|
+
return CMAKE_DECLARES_TESTS.test(cmakeCode(cmake)) ? {
|
|
33137
|
+
install: "cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON",
|
|
33138
|
+
test: "cmake --build build --parallel && ctest --test-dir build --output-on-failure"
|
|
33139
|
+
} : { install: null, test: null };
|
|
33140
|
+
}
|
|
33141
|
+
const meson = repo.readText("meson.build");
|
|
33142
|
+
if (meson !== null) {
|
|
33143
|
+
return /\btest\s*\(/.test(meson) ? { install: "meson setup build", test: "meson test -C build" } : { install: null, test: null };
|
|
33144
|
+
}
|
|
33145
|
+
const name = repo.listNames("").find((file) => MAKEFILE_NAMES.includes(file));
|
|
33146
|
+
const makefile = name === void 0 ? null : repo.readText(name);
|
|
33147
|
+
if (makefile !== null) {
|
|
33148
|
+
const targets = makeTargets(makefile);
|
|
33149
|
+
if (targets.has("test"))
|
|
33150
|
+
return { install: null, test: "make test" };
|
|
33151
|
+
if (targets.has("check"))
|
|
33152
|
+
return { install: null, test: "make check" };
|
|
33153
|
+
}
|
|
33154
|
+
return { install: null, test: null };
|
|
33155
|
+
}
|
|
32494
33156
|
function otherRuntimeCommands(repo, runtime) {
|
|
32495
33157
|
switch (runtime) {
|
|
32496
33158
|
case "go":
|
|
@@ -32518,11 +33180,24 @@ function otherRuntimeCommands(repo, runtime) {
|
|
|
32518
33180
|
case "dotnet":
|
|
32519
33181
|
return { install: "dotnet restore", test: "dotnet test" };
|
|
32520
33182
|
case "jvm": {
|
|
32521
|
-
if (repo.exists(
|
|
32522
|
-
return { install:
|
|
33183
|
+
if (!JVM_BUILD_FILES.some((file) => repo.exists(file)))
|
|
33184
|
+
return { install: null, test: null };
|
|
33185
|
+
if (repo.exists("pom.xml")) {
|
|
33186
|
+
return {
|
|
33187
|
+
install: `mvn -B dependency:go-offline && ${rehearse('mvn -B test -Dmaven.test.failure.ignore=true -Dmaven.repo.local="$R/.m2/repository"')}`,
|
|
33188
|
+
test: "mvn -B -o test"
|
|
33189
|
+
};
|
|
33190
|
+
}
|
|
32523
33191
|
const wrapper = repo.exists("gradlew") ? "./gradlew" : "gradle";
|
|
32524
|
-
return {
|
|
33192
|
+
return {
|
|
33193
|
+
install: `${wrapper} dependencies && ${rehearse(`${wrapper} test --continue`)}`,
|
|
33194
|
+
// `--no-build-cache`: the rehearsal above can fill Gradle's build cache, and a
|
|
33195
|
+
// test task restored FROM-CACHE runs nothing and prints no count.
|
|
33196
|
+
test: `${wrapper} test --offline --no-build-cache`
|
|
33197
|
+
};
|
|
32525
33198
|
}
|
|
33199
|
+
case "cpp":
|
|
33200
|
+
return cppCommands(repo);
|
|
32526
33201
|
default:
|
|
32527
33202
|
return { install: null, test: null };
|
|
32528
33203
|
}
|
|
@@ -32553,6 +33228,15 @@ function searchRuntimeVersion(repo, runtime) {
|
|
|
32553
33228
|
push(".ruby-version", repo.readText(".ruby-version"));
|
|
32554
33229
|
} else if (runtime === "dotnet") {
|
|
32555
33230
|
push("global.json sdk.version", readStringField(readObjectField(readJsonObject(repo, "global.json"), "sdk"), "version"));
|
|
33231
|
+
} else if (runtime === "jvm") {
|
|
33232
|
+
const pom = repo.readText("pom.xml");
|
|
33233
|
+
const gradle = repo.readText("build.gradle") ?? repo.readText("build.gradle.kts");
|
|
33234
|
+
push("build.gradle toolchain languageVersion", javaMajor(matchFirst(gradle, /JavaLanguageVersion\.of\(\s*["']?(\d+)["']?\s*\)/) ?? matchFirst(gradle, /jvmToolchain\(\s*(\d+)\s*\)/)));
|
|
33235
|
+
push(".java-version", javaMajor(repo.readText(".java-version")));
|
|
33236
|
+
push("pom.xml maven.compiler.release", javaMajor(matchFirst(pom, /<maven\.compiler\.release>\s*([^<\s]+)\s*</)));
|
|
33237
|
+
push("pom.xml java.version", javaMajor(matchFirst(pom, /<java\.version>\s*([^<\s]+)\s*</)));
|
|
33238
|
+
push("pom.xml maven.compiler.source", javaMajor(matchFirst(pom, /<maven\.compiler\.source>\s*([^<\s]+)\s*</)));
|
|
33239
|
+
push("build.gradle sourceCompatibility", javaMajor(matchFirst(gradle, /sourceCompatibility\s*=\s*JavaVersion\.VERSION_([\d_]+)/)?.replace("_", ".") ?? matchFirst(gradle, /sourceCompatibility\s*=\s*["']?([\d.]+)["']?/)));
|
|
32556
33240
|
}
|
|
32557
33241
|
let rangeFound = null;
|
|
32558
33242
|
for (const source of sources) {
|
|
@@ -32566,13 +33250,21 @@ function searchRuntimeVersion(repo, runtime) {
|
|
|
32566
33250
|
}
|
|
32567
33251
|
return { version: null, searched: sources.map((s) => s.label), rangeFound };
|
|
32568
33252
|
}
|
|
33253
|
+
function javaMajor(text) {
|
|
33254
|
+
if (text === null)
|
|
33255
|
+
return null;
|
|
33256
|
+
const match2 = /^(?:1\.(\d+)|(\d+))(?:[._][\d_.]*)?$/.exec(text.trim());
|
|
33257
|
+
if (match2 === null)
|
|
33258
|
+
return null;
|
|
33259
|
+
return match2[1] ?? match2[2] ?? null;
|
|
33260
|
+
}
|
|
32569
33261
|
function matchFirst(text, pattern) {
|
|
32570
33262
|
if (text === null)
|
|
32571
33263
|
return null;
|
|
32572
33264
|
const match2 = pattern.exec(text);
|
|
32573
33265
|
return match2 === null ? null : match2[1];
|
|
32574
33266
|
}
|
|
32575
|
-
var RUNTIME_MANIFESTS, MANIFEST_FILENAMES, NPM_PLACEHOLDER_TEST, REQUIREMENT_SPECIFIER, EXACT_VERSION;
|
|
33267
|
+
var RUNTIME_MANIFESTS, MAKEFILE_NAMES, C_FAMILY_SOURCE, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, REQUIREMENT_SPECIFIER, CPP_BUILD_FILES, CMAKE_DECLARES_TESTS, EXACT_VERSION;
|
|
32576
33268
|
var init_manifest2 = __esm({
|
|
32577
33269
|
"../../packages/envspec/dist/manifest.js"() {
|
|
32578
33270
|
"use strict";
|
|
@@ -32583,11 +33275,19 @@ var init_manifest2 = __esm({
|
|
|
32583
33275
|
{ runtime: "rust", files: ["Cargo.toml"] },
|
|
32584
33276
|
{ runtime: "jvm", files: ["pom.xml", "build.gradle", "build.gradle.kts"] },
|
|
32585
33277
|
{ runtime: "ruby", files: ["Gemfile"] },
|
|
32586
|
-
{ runtime: "dotnet", files: [] }
|
|
33278
|
+
{ runtime: "dotnet", files: [] },
|
|
33279
|
+
// Last, so an earlier runtime keeps precedence: a Python package with a C
|
|
33280
|
+
// extension built by CMake is still a Python repo, with cpp recorded second.
|
|
33281
|
+
{ runtime: "cpp", files: ["CMakeLists.txt", "meson.build"] }
|
|
32587
33282
|
];
|
|
32588
|
-
|
|
33283
|
+
MAKEFILE_NAMES = ["GNUmakefile", "Makefile", "makefile"];
|
|
33284
|
+
C_FAMILY_SOURCE = /\.(?:c|cc|cpp|cxx|h|hpp|hh)$/i;
|
|
33285
|
+
MANIFEST_FILENAMES = RUNTIME_MANIFESTS.flatMap((m) => m.files).concat(["*.csproj", "*.fsproj", "*.sln"], MAKEFILE_NAMES).sort();
|
|
33286
|
+
JVM_BUILD_FILES = ["build.gradle", "build.gradle.kts", "pom.xml"];
|
|
32589
33287
|
NPM_PLACEHOLDER_TEST = /^echo\s+["']?Error:\s*no test specified["']?\s*&&\s*exit\s+1$/;
|
|
32590
33288
|
REQUIREMENT_SPECIFIER = /^[A-Za-z0-9._-]+(\[[A-Za-z0-9._,-]+\])?([<>=!~]=?[A-Za-z0-9._*+-]+(,[<>=!~]=?[A-Za-z0-9._*+-]+)*)?$/;
|
|
33289
|
+
CPP_BUILD_FILES = ["CMakeLists.txt", "meson.build", ...MAKEFILE_NAMES];
|
|
33290
|
+
CMAKE_DECLARES_TESTS = /\benable_testing\s*\(|\binclude\s*\(\s*CTest\s*\)|\badd_test\s*\(/;
|
|
32591
33291
|
EXACT_VERSION = /^v?(\d+(?:\.\d+){0,2})$/;
|
|
32592
33292
|
}
|
|
32593
33293
|
});
|
|
@@ -32660,11 +33360,11 @@ var init_references = __esm({
|
|
|
32660
33360
|
});
|
|
32661
33361
|
|
|
32662
33362
|
// ../../packages/envspec/dist/repo.js
|
|
32663
|
-
import { readdirSync as
|
|
32664
|
-
import { join as join25, relative as relative2, sep as
|
|
33363
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
|
|
33364
|
+
import { join as join25, relative as relative2, sep as sep6 } from "path";
|
|
32665
33365
|
function createRepoReader(repoPath) {
|
|
32666
33366
|
const resolveIn = (relativePath) => relativePath === "" ? repoPath : join25(repoPath, relativePath);
|
|
32667
|
-
const toPosix = (absolute) => relative2(repoPath, absolute).split(
|
|
33367
|
+
const toPosix = (absolute) => relative2(repoPath, absolute).split(sep6).join("/");
|
|
32668
33368
|
const readText = (relativePath) => {
|
|
32669
33369
|
try {
|
|
32670
33370
|
return readFileSync12(resolveIn(relativePath), "utf8");
|
|
@@ -32674,7 +33374,7 @@ function createRepoReader(repoPath) {
|
|
|
32674
33374
|
};
|
|
32675
33375
|
const statOf = (relativePath) => {
|
|
32676
33376
|
try {
|
|
32677
|
-
return
|
|
33377
|
+
return statSync5(resolveIn(relativePath));
|
|
32678
33378
|
} catch {
|
|
32679
33379
|
return null;
|
|
32680
33380
|
}
|
|
@@ -32686,7 +33386,7 @@ function createRepoReader(repoPath) {
|
|
|
32686
33386
|
return;
|
|
32687
33387
|
let names;
|
|
32688
33388
|
try {
|
|
32689
|
-
names =
|
|
33389
|
+
names = readdirSync3(dir);
|
|
32690
33390
|
} catch {
|
|
32691
33391
|
return;
|
|
32692
33392
|
}
|
|
@@ -32721,7 +33421,7 @@ function createRepoReader(repoPath) {
|
|
|
32721
33421
|
if (st === null || !st.isDirectory())
|
|
32722
33422
|
return [];
|
|
32723
33423
|
try {
|
|
32724
|
-
return
|
|
33424
|
+
return readdirSync3(resolveIn(relativeDir)).slice().sort();
|
|
32725
33425
|
} catch {
|
|
32726
33426
|
return [];
|
|
32727
33427
|
}
|
|
@@ -32982,7 +33682,7 @@ function deriveFromReader(repo) {
|
|
|
32982
33682
|
searched: MANIFEST_FILENAMES,
|
|
32983
33683
|
detail: `no ecosystem manifest was found at the repo root (looked for ${MANIFEST_FILENAMES.join(", ")}), so the language runtime could not be identified`
|
|
32984
33684
|
});
|
|
32985
|
-
} else {
|
|
33685
|
+
} else if (runtime !== "jvm" || JVM_BUILD_FILES.some((file) => repo.exists(file))) {
|
|
32986
33686
|
derivedFrom.add("manifest");
|
|
32987
33687
|
}
|
|
32988
33688
|
if (detection.candidates.length > 1) {
|
|
@@ -33046,7 +33746,7 @@ function deriveFromReader(repo) {
|
|
|
33046
33746
|
});
|
|
33047
33747
|
}
|
|
33048
33748
|
const version = deriveRuntimeVersion(repo, runtime, job);
|
|
33049
|
-
if (version.value === null && runtime !== "unknown") {
|
|
33749
|
+
if (version.value === null && runtime !== "unknown" && runtime !== "cpp") {
|
|
33050
33750
|
unresolved.push({
|
|
33051
33751
|
kind: "runtime-version-undeclared",
|
|
33052
33752
|
runtime,
|
|
@@ -33146,12 +33846,13 @@ function deriveRuntimeVersion(repo, runtime, job) {
|
|
|
33146
33846
|
const setup = SETUP_ACTIONS[runtime];
|
|
33147
33847
|
const searched = [];
|
|
33148
33848
|
let rangeFound = null;
|
|
33849
|
+
const pin = (raw) => runtime === "jvm" ? javaMajor(raw) : exactVersion(raw);
|
|
33149
33850
|
if (job !== null && setup !== void 0) {
|
|
33150
33851
|
const direct = findActionInput(job, setup.action, setup.input);
|
|
33151
33852
|
if (direct !== null) {
|
|
33152
33853
|
searched.push(`${job.file} ${setup.action} ${setup.input}`);
|
|
33153
33854
|
const reduced = reduceExpression(direct.command, job.singletons);
|
|
33154
|
-
const exact =
|
|
33855
|
+
const exact = pin(reduced);
|
|
33155
33856
|
if (exact !== null)
|
|
33156
33857
|
return { value: exact, searched, rangeFound: null };
|
|
33157
33858
|
if (reduced !== null)
|
|
@@ -33160,7 +33861,7 @@ function deriveRuntimeVersion(repo, runtime, job) {
|
|
|
33160
33861
|
const viaFile = findActionInput(job, setup.action, setup.fileInput);
|
|
33161
33862
|
if (viaFile !== null) {
|
|
33162
33863
|
searched.push(`${job.file} ${setup.action} ${setup.fileInput} -> ${viaFile.command}`);
|
|
33163
|
-
const exact =
|
|
33864
|
+
const exact = pin(repo.readText(viaFile.command));
|
|
33164
33865
|
if (exact !== null)
|
|
33165
33866
|
return { value: exact, searched, rangeFound: null };
|
|
33166
33867
|
}
|
|
@@ -33185,6 +33886,10 @@ function testSearchPaths(runtime, workflowFiles) {
|
|
|
33185
33886
|
paths.push("**/*_test.go");
|
|
33186
33887
|
if (runtime === "ruby")
|
|
33187
33888
|
paths.push("Gemfile", "Rakefile");
|
|
33889
|
+
if (runtime === "jvm")
|
|
33890
|
+
paths.push(...JVM_BUILD_FILES);
|
|
33891
|
+
if (runtime === "cpp")
|
|
33892
|
+
paths.push(...CPP_BUILD_FILES);
|
|
33188
33893
|
if (runtime === "unknown")
|
|
33189
33894
|
paths.push(...MANIFEST_FILENAMES);
|
|
33190
33895
|
return [...new Set(paths)].sort();
|
|
@@ -33202,14 +33907,37 @@ function describeTestSearch(repo, runtime, workflowFiles) {
|
|
|
33202
33907
|
if (runtime === "python") {
|
|
33203
33908
|
return `${ciPart}, and nothing in the repo names a test runner \u2014 no \`[tool.pytest.ini_options]\`, no \`pytest.ini\`, and no pytest dependency in \`pyproject.toml\` or a requirements file`;
|
|
33204
33909
|
}
|
|
33910
|
+
if (runtime === "jvm" && !JVM_BUILD_FILES.some((file) => repo.exists(file))) {
|
|
33911
|
+
return `${ciPart}, and the repo has Java sources but no build file (${JVM_BUILD_FILES.join(", ")}), so there is no build to run tests with. Compiling the sources with \`javac\` alone would run no tests`;
|
|
33912
|
+
}
|
|
33913
|
+
if (runtime === "cpp")
|
|
33914
|
+
return `${ciPart}, and ${describeCppSearch(repo)}`;
|
|
33205
33915
|
return `${ciPart}, and no ${runtime} test convention applies to this tree`;
|
|
33206
33916
|
}
|
|
33917
|
+
function describeCppSearch(repo) {
|
|
33918
|
+
const cmake = repo.readText("CMakeLists.txt");
|
|
33919
|
+
if (cmake !== null && !CMAKE_DECLARES_TESTS.test(cmakeCode(cmake))) {
|
|
33920
|
+
return "`CMakeLists.txt` registers no tests: no `enable_testing()`, `include(CTest)` or `add_test()`";
|
|
33921
|
+
}
|
|
33922
|
+
if (cmake === null && repo.exists("meson.build")) {
|
|
33923
|
+
return "`meson.build` declares no `test()`";
|
|
33924
|
+
}
|
|
33925
|
+
const makefile = repo.listNames("").find((name) => MAKEFILE_NAMES.includes(name));
|
|
33926
|
+
if (cmake === null && makefile !== void 0) {
|
|
33927
|
+
return `\`${makefile}\` has no \`test\` or \`check\` target`;
|
|
33928
|
+
}
|
|
33929
|
+
return `no C/C++ test convention applies to this tree (looked in ${CPP_BUILD_FILES.join(", ")})`;
|
|
33930
|
+
}
|
|
33207
33931
|
function installSearchPaths(runtime, workflowFiles) {
|
|
33208
33932
|
const paths = [...workflowFiles];
|
|
33209
33933
|
if (runtime === "node")
|
|
33210
33934
|
paths.push("package-lock.json", "pnpm-lock.yaml", "yarn.lock");
|
|
33211
33935
|
if (runtime === "python")
|
|
33212
33936
|
paths.push("poetry.lock", "pyproject.toml", "requirements.txt", "uv.lock");
|
|
33937
|
+
if (runtime === "jvm")
|
|
33938
|
+
paths.push(...JVM_BUILD_FILES);
|
|
33939
|
+
if (runtime === "cpp")
|
|
33940
|
+
paths.push(...CPP_BUILD_FILES);
|
|
33213
33941
|
if (runtime === "unknown")
|
|
33214
33942
|
paths.push(...MANIFEST_FILENAMES);
|
|
33215
33943
|
return [...new Set(paths)].sort();
|
|
@@ -33288,12 +34016,46 @@ var init_dist3 = __esm({
|
|
|
33288
34016
|
}
|
|
33289
34017
|
});
|
|
33290
34018
|
|
|
34019
|
+
// ../../packages/envrun/dist/gradleTestSummary.js
|
|
34020
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
34021
|
+
import { join as join26 } from "path";
|
|
34022
|
+
function stageGradleTestSummary(jailHome, guestUser, io = {}) {
|
|
34023
|
+
const dir = join26(jailHome, ".gradle", "init.d");
|
|
34024
|
+
mkdirSync5(dir, { recursive: true });
|
|
34025
|
+
writeFileSync14(join26(dir, "terminalhire-test-summary.gradle"), GRADLE_TEST_SUMMARY_INIT);
|
|
34026
|
+
const ids2 = io.ids === void 0 ? hostIds() : io.ids;
|
|
34027
|
+
if (ids2?.uid === 0 && guestUser && guestUser.uid !== 0) {
|
|
34028
|
+
(io.chown ?? chownTree)(join26(jailHome, ".gradle"), guestUser);
|
|
34029
|
+
}
|
|
34030
|
+
}
|
|
34031
|
+
var GRADLE_TEST_MARKER, GRADLE_TEST_SUMMARY_INIT;
|
|
34032
|
+
var init_gradleTestSummary = __esm({
|
|
34033
|
+
"../../packages/envrun/dist/gradleTestSummary.js"() {
|
|
34034
|
+
"use strict";
|
|
34035
|
+
init_dist();
|
|
34036
|
+
GRADLE_TEST_MARKER = "terminalhire-gradle-tests:";
|
|
34037
|
+
GRADLE_TEST_SUMMARY_INIT = `// Written by terminalhire (TERM-1122): prints each Test task's counts, because
|
|
34038
|
+
// Gradle prints none when every test passes.
|
|
34039
|
+
allprojects {
|
|
34040
|
+
tasks.withType(Test).configureEach { task ->
|
|
34041
|
+
def label = task.path
|
|
34042
|
+
task.afterSuite { desc, result ->
|
|
34043
|
+
if (desc.parent == null) {
|
|
34044
|
+
println "${GRADLE_TEST_MARKER} task=\${label} total=\${result.testCount} passed=\${result.successfulTestCount} failed=\${result.failedTestCount} skipped=\${result.skippedTestCount}"
|
|
34045
|
+
}
|
|
34046
|
+
}
|
|
34047
|
+
}
|
|
34048
|
+
}
|
|
34049
|
+
`;
|
|
34050
|
+
}
|
|
34051
|
+
});
|
|
34052
|
+
|
|
33291
34053
|
// ../../packages/envrun/dist/thrun.js
|
|
33292
34054
|
import { execFileSync, spawnSync as spawnSync6 } from "child_process";
|
|
33293
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
34055
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync4, rmSync as rmSync9 } from "fs";
|
|
33294
34056
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
33295
34057
|
import { tmpdir as tmpdir4 } from "os";
|
|
33296
|
-
import { join as
|
|
34058
|
+
import { join as join27 } from "path";
|
|
33297
34059
|
function git(repoDir, args, allowNonZero = false) {
|
|
33298
34060
|
const res = spawnSync6("git", [...args], {
|
|
33299
34061
|
cwd: repoDir,
|
|
@@ -33308,7 +34070,7 @@ function git(repoDir, args, allowNonZero = false) {
|
|
|
33308
34070
|
return res.stdout ?? "";
|
|
33309
34071
|
}
|
|
33310
34072
|
function collectWorkingDiff(repoDir, opts = {}) {
|
|
33311
|
-
if (!existsSync12(
|
|
34073
|
+
if (!existsSync12(join27(repoDir, ".git"))) {
|
|
33312
34074
|
throw new ThRunError(`${repoDir} is not a git checkout (no .git). \`th run\` ships the working diff, so it needs a repository to read one from.`);
|
|
33313
34075
|
}
|
|
33314
34076
|
const headSha = git(repoDir, ["rev-parse", "HEAD"]).trim();
|
|
@@ -33456,7 +34218,7 @@ function credentialFreeHome() {
|
|
|
33456
34218
|
return credentialFreeHomeDir;
|
|
33457
34219
|
let made;
|
|
33458
34220
|
try {
|
|
33459
|
-
made = mkdtempSync4(
|
|
34221
|
+
made = mkdtempSync4(join27(tmpdir4(), "th-run-nohome-"));
|
|
33460
34222
|
} catch (err) {
|
|
33461
34223
|
throw new RunRefusalError("could not create the empty directory this clone uses as its home, so the clone would read the credentials on this machine instead. That is our environment failing, not your tests: check that the temp directory is writable.", { cause: err });
|
|
33462
34224
|
}
|
|
@@ -33531,7 +34293,7 @@ function cloneTargetAtUnguarded(opts) {
|
|
|
33531
34293
|
if (persisted !== null) {
|
|
33532
34294
|
throw new RunRefusalError(`refusing to clone from a URL carrying ${persisted}: \`git remote add\` writes the source verbatim into .git/config, which is mounted where the repo\u2019s own test command runs. Fetch with the credential out of band so it is never written to disk \u2014 this runner takes one as an HTTP header, which is never persisted.`);
|
|
33533
34295
|
}
|
|
33534
|
-
|
|
34296
|
+
mkdirSync6(opts.dest, { recursive: true });
|
|
33535
34297
|
const runOut = (args) => execFileSync("git", [...gitConfigArgs(), ...args], {
|
|
33536
34298
|
cwd: opts.dest,
|
|
33537
34299
|
encoding: "utf8",
|
|
@@ -33570,7 +34332,7 @@ function cloneTargetAtUnguarded(opts) {
|
|
|
33570
34332
|
}
|
|
33571
34333
|
function scrubCloneSource(dest, run2) {
|
|
33572
34334
|
run2(["remote", "remove", "origin"]);
|
|
33573
|
-
rmSync9(
|
|
34335
|
+
rmSync9(join27(dest, ".git", "FETCH_HEAD"), { force: true });
|
|
33574
34336
|
}
|
|
33575
34337
|
function publishableTarget(url) {
|
|
33576
34338
|
if (separatorInTarget(url) !== null)
|
|
@@ -33718,6 +34480,9 @@ function refusedRun(fields) {
|
|
|
33718
34480
|
containerImage: null,
|
|
33719
34481
|
containerImageDigest: null,
|
|
33720
34482
|
leaksClean: null,
|
|
34483
|
+
// Null for the same reason, and never `unobserved`: that word says we tried to take a
|
|
34484
|
+
// census and could not, and here there was nothing to take one of (TERM-1144).
|
|
34485
|
+
leakState: null,
|
|
33721
34486
|
// A refused run never held a lease, so there is no venue to describe. Same
|
|
33722
34487
|
// reasoning as `leaksClean` above: null because nothing happened, and it must
|
|
33723
34488
|
// not read as a venue we looked at and could not name.
|
|
@@ -33855,7 +34620,7 @@ async function verifyWorkingDiff(req) {
|
|
|
33855
34620
|
}
|
|
33856
34621
|
async function runVerification(req, ctx) {
|
|
33857
34622
|
const { runId, startedAt } = ctx;
|
|
33858
|
-
const labels =
|
|
34623
|
+
const labels = runLabels(runId, req.labels);
|
|
33859
34624
|
const progress = req.onProgress ?? (() => {
|
|
33860
34625
|
});
|
|
33861
34626
|
const badTarget = unacceptableTarget(req);
|
|
@@ -33945,10 +34710,10 @@ async function runVerification(req, ctx) {
|
|
|
33945
34710
|
venueIdentity: null
|
|
33946
34711
|
};
|
|
33947
34712
|
}
|
|
33948
|
-
const stage =
|
|
33949
|
-
const cloneDir =
|
|
33950
|
-
const scratch =
|
|
33951
|
-
|
|
34713
|
+
const stage = join27(req.scratchRoot, runId);
|
|
34714
|
+
const cloneDir = join27(stage, "clone");
|
|
34715
|
+
const scratch = join27(stage, "scratch");
|
|
34716
|
+
mkdirSync6(scratch, { recursive: true });
|
|
33952
34717
|
assertSafeTargetSha(req.targetSha);
|
|
33953
34718
|
progress("clone", `${publishableTarget(req.targetRepo)} @ ${req.targetSha.slice(0, 12)}`);
|
|
33954
34719
|
cloneTargetAt({
|
|
@@ -33975,7 +34740,8 @@ async function runVerification(req, ctx) {
|
|
|
33975
34740
|
const derived = deriveEnvironmentSpec(cloneDir);
|
|
33976
34741
|
const spec = req.testCommandOverride === void 0 ? derived : { ...derived, testCommand: req.testCommandOverride };
|
|
33977
34742
|
progress("derive", `runtime=${spec.runtime} install=${String(spec.installCommand)} test=${String(spec.testCommand)}`);
|
|
33978
|
-
|
|
34743
|
+
refuseUnbuildableSpec(spec);
|
|
34744
|
+
const image = placement.imageFor(spec.runtime, req.image, spec.runtimeVersion, imageVariantFor(spec));
|
|
33979
34745
|
const resolved = await resolveLease(placement, runId);
|
|
33980
34746
|
if (!resolved.ok) {
|
|
33981
34747
|
return {
|
|
@@ -34009,11 +34775,13 @@ async function runVerification(req, ctx) {
|
|
|
34009
34775
|
}
|
|
34010
34776
|
const lease = resolved.lease;
|
|
34011
34777
|
try {
|
|
34012
|
-
buildJail(scratch);
|
|
34778
|
+
buildJail(scratch, lease.guestUser);
|
|
34779
|
+
if (spec.runtime === "jvm")
|
|
34780
|
+
stageGradleTestSummary(localJailPaths(scratch).jail, lease.guestUser);
|
|
34013
34781
|
const venuePaths = await lease.stage({
|
|
34014
34782
|
cloneDir,
|
|
34015
34783
|
scratchRoot: scratch,
|
|
34016
|
-
previewDir:
|
|
34784
|
+
previewDir: join27(stage, "preview"),
|
|
34017
34785
|
// On a dispatched run the commit is the statement of what was tested, so
|
|
34018
34786
|
// it rides with the tree and the venue seam refuses a tree that is not
|
|
34019
34787
|
// that commit (design §6 item 4, TERM-892 — the guard lives in
|
|
@@ -34077,6 +34845,7 @@ async function runVerification(req, ctx) {
|
|
|
34077
34845
|
// the operator would sign our name onto their choice.
|
|
34078
34846
|
imageSource: req.image === void 0 ? "detected" : req.imageOrigin === "operator" ? "operator-declared" : "developer-declared",
|
|
34079
34847
|
leaksClean: verdict.leaks.clean,
|
|
34848
|
+
leakState: verdict.leaks.state,
|
|
34080
34849
|
// Built from the LEASE, over the client that ran the steps — never from
|
|
34081
34850
|
// `req.placement`, which is a request. `venueDescriptor.ts` carries the
|
|
34082
34851
|
// reasoning and the #735 failure that makes the distinction load-bearing.
|
|
@@ -34134,6 +34903,7 @@ var init_thrun = __esm({
|
|
|
34134
34903
|
init_execute();
|
|
34135
34904
|
init_placement();
|
|
34136
34905
|
init_venue();
|
|
34906
|
+
init_gradleTestSummary();
|
|
34137
34907
|
init_venueDescriptor();
|
|
34138
34908
|
init_result();
|
|
34139
34909
|
ThRunError = class extends Error {
|
|
@@ -34271,14 +35041,14 @@ function migrationUnits(runner, migrations) {
|
|
|
34271
35041
|
return [...byDir.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([id, path5]) => ({ id, path: path5 }));
|
|
34272
35042
|
}
|
|
34273
35043
|
case "alembic":
|
|
34274
|
-
return migrations.filter((p) => /^alembic\/versions\/[^/]+\.py$/.test(p) && !p.endsWith("/__init__.py")).sort().map((path5) => ({ id:
|
|
35044
|
+
return migrations.filter((p) => /^alembic\/versions\/[^/]+\.py$/.test(p) && !p.endsWith("/__init__.py")).sort().map((path5) => ({ id: basename5(path5).replace(/\.py$/, ""), path: path5 }));
|
|
34275
35045
|
case "rails":
|
|
34276
|
-
return migrations.filter((p) => /^db\/migrate\/[^/]+\.rb$/.test(p)).sort().map((path5) => ({ id: /^(\d+)/.exec(
|
|
35046
|
+
return migrations.filter((p) => /^db\/migrate\/[^/]+\.rb$/.test(p)).sort().map((path5) => ({ id: /^(\d+)/.exec(basename5(path5))?.[1] ?? basename5(path5), path: path5 }));
|
|
34277
35047
|
case "sql":
|
|
34278
35048
|
return migrations.filter((p) => p.endsWith(".sql") && !p.startsWith("prisma/migrations/")).sort().map((path5) => ({ id: path5, path: path5 }));
|
|
34279
35049
|
}
|
|
34280
35050
|
}
|
|
34281
|
-
function
|
|
35051
|
+
function basename5(path5) {
|
|
34282
35052
|
const at = path5.lastIndexOf("/");
|
|
34283
35053
|
return at === -1 ? path5 : path5.slice(at + 1);
|
|
34284
35054
|
}
|
|
@@ -34376,8 +35146,8 @@ var init_dbplan = __esm({
|
|
|
34376
35146
|
|
|
34377
35147
|
// ../../packages/envrun/dist/dbstack.js
|
|
34378
35148
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
34379
|
-
import { randomBytes as
|
|
34380
|
-
import { mkdirSync as
|
|
35149
|
+
import { randomBytes as randomBytes12 } from "crypto";
|
|
35150
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
34381
35151
|
function installCommandFor(runner) {
|
|
34382
35152
|
switch (runner) {
|
|
34383
35153
|
case "sql":
|
|
@@ -34412,7 +35182,7 @@ function docker2(args, timeoutMs = DOCKER_TIMEOUT_MS2) {
|
|
|
34412
35182
|
function generateCredentials(host) {
|
|
34413
35183
|
return {
|
|
34414
35184
|
user: "thverify",
|
|
34415
|
-
password:
|
|
35185
|
+
password: randomBytes12(24).toString("base64url"),
|
|
34416
35186
|
database: "thverify",
|
|
34417
35187
|
host,
|
|
34418
35188
|
port: 5432
|
|
@@ -34608,7 +35378,7 @@ function startDatabase(req) {
|
|
|
34608
35378
|
if (!ready.ok) {
|
|
34609
35379
|
throw new DbStackError(`INFRASTRUCTURE/INCONCLUSIVE: Postgres never accepted queries at 127.0.0.1:${String(creds.port)} within the readiness window: ${ready.detail}`);
|
|
34610
35380
|
}
|
|
34611
|
-
const
|
|
35381
|
+
const query2 = (sql) => {
|
|
34612
35382
|
const res = docker2([
|
|
34613
35383
|
"exec",
|
|
34614
35384
|
`--env=PGPASSWORD=${creds.password}`,
|
|
@@ -34656,7 +35426,7 @@ function startDatabase(req) {
|
|
|
34656
35426
|
credentials: creds,
|
|
34657
35427
|
image,
|
|
34658
35428
|
readyMs: ready.ms,
|
|
34659
|
-
query,
|
|
35429
|
+
query: query2,
|
|
34660
35430
|
runOnNetwork,
|
|
34661
35431
|
teardown
|
|
34662
35432
|
};
|
|
@@ -34719,8 +35489,8 @@ async function installLocalMigrationTooling(opts) {
|
|
|
34719
35489
|
};
|
|
34720
35490
|
}
|
|
34721
35491
|
const { jail, tmp } = buildJail(opts.scratchRoot);
|
|
34722
|
-
|
|
34723
|
-
|
|
35492
|
+
mkdirSync7(jail, { recursive: true });
|
|
35493
|
+
mkdirSync7(tmp, { recursive: true });
|
|
34724
35494
|
const spec = {
|
|
34725
35495
|
profile: "install",
|
|
34726
35496
|
clone: opts.repoDir,
|
|
@@ -35015,6 +35785,7 @@ __export(dist_exports, {
|
|
|
35015
35785
|
GOOGLE_JWKS_URL: () => GOOGLE_JWKS_URL,
|
|
35016
35786
|
HostedVenueError: () => HostedVenueError,
|
|
35017
35787
|
JWKS_FETCH_TIMEOUT_MS: () => JWKS_FETCH_TIMEOUT_MS,
|
|
35788
|
+
LEAK_STATES: () => LEAK_STATES,
|
|
35018
35789
|
LOCAL_MEASUREMENT_PREFIX: () => LOCAL_MEASUREMENT_PREFIX,
|
|
35019
35790
|
LabelWatch: () => LabelWatch,
|
|
35020
35791
|
MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
|
|
@@ -35031,6 +35802,7 @@ __export(dist_exports, {
|
|
|
35031
35802
|
REDACTED_TARGET_SHA: () => REDACTED_TARGET_SHA,
|
|
35032
35803
|
RELEASED_LEASE_CENSUS_REASON: () => RELEASED_LEASE_CENSUS_REASON,
|
|
35033
35804
|
REPO_DIGEST_RE: () => REPO_DIGEST_RE,
|
|
35805
|
+
RUN_ID_LABEL_KEY: () => RUN_ID_LABEL_KEY,
|
|
35034
35806
|
RUN_IMAGE_SOURCES: () => RUN_IMAGE_SOURCES,
|
|
35035
35807
|
RUN_LABEL_KEY: () => RUN_LABEL_KEY,
|
|
35036
35808
|
RUN_RESULT_FIELDS: () => RUN_RESULT_FIELDS,
|
|
@@ -35048,6 +35820,7 @@ __export(dist_exports, {
|
|
|
35048
35820
|
VENUE_GCLOUD_CONFIG: () => VENUE_GCLOUD_CONFIG,
|
|
35049
35821
|
VENUE_SSH_USER: () => VENUE_SSH_USER,
|
|
35050
35822
|
VENV_DIR: () => VENV_DIR,
|
|
35823
|
+
VERIFICATION_OUTCOMES: () => VERIFICATION_OUTCOMES,
|
|
35051
35824
|
VenueRollbackError: () => VenueRollbackError,
|
|
35052
35825
|
acquireTransactionally: () => acquireTransactionally,
|
|
35053
35826
|
alembicChainPosition: () => alembicChainPosition,
|
|
@@ -35061,6 +35834,7 @@ __export(dist_exports, {
|
|
|
35061
35834
|
assertVenueOwnerDeclared: () => assertVenueOwnerDeclared,
|
|
35062
35835
|
bookkeepingFor: () => bookkeepingFor,
|
|
35063
35836
|
census: () => census,
|
|
35837
|
+
censusReport: () => censusReport,
|
|
35064
35838
|
censusTotal: () => censusTotal,
|
|
35065
35839
|
classifyBootFailure: () => classifyBootFailure,
|
|
35066
35840
|
classifyProbeFailure: () => classifyProbeFailure,
|
|
@@ -35080,6 +35854,7 @@ __export(dist_exports, {
|
|
|
35080
35854
|
detectRunner: () => detectRunner,
|
|
35081
35855
|
endOfOptionsUnsupported: () => endOfOptionsUnsupported,
|
|
35082
35856
|
ensureVenueServiceCredentials: () => ensureVenueServiceCredentials,
|
|
35857
|
+
exitCodeFor: () => exitCodeFor,
|
|
35083
35858
|
failureSourceOf: () => failureSourceOf,
|
|
35084
35859
|
findRunRefusal: () => findRunRefusal,
|
|
35085
35860
|
gcpBootArgv: () => gcpBootArgv,
|
|
@@ -35094,11 +35869,13 @@ __export(dist_exports, {
|
|
|
35094
35869
|
identityProbeCommand: () => identityProbeCommand,
|
|
35095
35870
|
imageForRuntime: () => imageForRuntime,
|
|
35096
35871
|
imageRepo: () => imageRepo,
|
|
35872
|
+
imageVariantFor: () => imageVariantFor,
|
|
35097
35873
|
installCommandFor: () => installCommandFor,
|
|
35098
35874
|
installLocalMigrationTooling: () => installLocalMigrationTooling,
|
|
35099
35875
|
isBookkeepingTable: () => isBookkeepingTable,
|
|
35100
35876
|
isCommandUnavailable: () => isCommandUnavailable,
|
|
35101
35877
|
isGreen: () => isGreen,
|
|
35878
|
+
isMissingSystemDependency: () => isMissingSystemDependency,
|
|
35102
35879
|
isOurFault: () => isOurFault,
|
|
35103
35880
|
judgeCompleteness: () => judgeCompleteness,
|
|
35104
35881
|
judgeLeaks: () => judgeLeaks,
|
|
@@ -35123,12 +35900,14 @@ __export(dist_exports, {
|
|
|
35123
35900
|
readSchema: () => readSchema,
|
|
35124
35901
|
recordedApplied: () => recordedApplied,
|
|
35125
35902
|
refuseSshTransport: () => refuseSshTransport,
|
|
35903
|
+
refuseUnbuildableSpec: () => refuseUnbuildableSpec,
|
|
35126
35904
|
renderRunReport: () => renderRunReport,
|
|
35127
35905
|
renderVenueLine: () => renderVenueLine,
|
|
35128
35906
|
renderVerdictLine: () => renderVerdictLine,
|
|
35129
35907
|
resolveImageForSpec: () => resolveImageForSpec,
|
|
35130
35908
|
resolveLease: () => resolveLease,
|
|
35131
35909
|
runEnvironmentSpec: () => runEnvironmentSpec,
|
|
35910
|
+
runLabels: () => runLabels,
|
|
35132
35911
|
setManifestProbe: () => setManifestProbe,
|
|
35133
35912
|
sha256Hex: () => sha256Hex,
|
|
35134
35913
|
signRunStatement: () => signRunStatement,
|
|
@@ -35209,8 +35988,8 @@ __export(cache_store_exports, {
|
|
|
35209
35988
|
updateIndexCache: () => updateIndexCache,
|
|
35210
35989
|
writeIndexCache: () => writeIndexCache
|
|
35211
35990
|
});
|
|
35212
|
-
import { readFileSync as readFileSync13, writeFileSync as
|
|
35213
|
-
import { join as
|
|
35991
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync15, renameSync as renameSync5 } from "fs";
|
|
35992
|
+
import { join as join28 } from "path";
|
|
35214
35993
|
import { homedir as homedir13 } from "os";
|
|
35215
35994
|
function readCacheEntry() {
|
|
35216
35995
|
try {
|
|
@@ -35229,7 +36008,7 @@ function updateIndexCache(patch) {
|
|
|
35229
36008
|
ts: Date.now()
|
|
35230
36009
|
};
|
|
35231
36010
|
const tmp = `${INDEX_CACHE_FILE}.${process.pid}.${tmpCounter++}.tmp`;
|
|
35232
|
-
|
|
36011
|
+
writeFileSync15(tmp, JSON.stringify(entry), "utf8");
|
|
35233
36012
|
renameSync5(tmp, INDEX_CACHE_FILE);
|
|
35234
36013
|
return entry;
|
|
35235
36014
|
}
|
|
@@ -35241,8 +36020,8 @@ var init_cache_store = __esm({
|
|
|
35241
36020
|
"bin/cache-store.js"() {
|
|
35242
36021
|
"use strict";
|
|
35243
36022
|
init_state_dir();
|
|
35244
|
-
TERMINALHIRE_DIR10 = process.env.TERMINALHIRE_DIR ||
|
|
35245
|
-
INDEX_CACHE_FILE =
|
|
36023
|
+
TERMINALHIRE_DIR10 = process.env.TERMINALHIRE_DIR || join28(homedir13(), ".terminalhire");
|
|
36024
|
+
INDEX_CACHE_FILE = join28(TERMINALHIRE_DIR10, "index-cache.json");
|
|
35246
36025
|
SCHEMA_VERSION2 = 1;
|
|
35247
36026
|
tmpCounter = 0;
|
|
35248
36027
|
}
|
|
@@ -35442,17 +36221,17 @@ var init_audit2 = __esm({
|
|
|
35442
36221
|
init_src();
|
|
35443
36222
|
import {
|
|
35444
36223
|
readFileSync as readFileSync14,
|
|
35445
|
-
writeFileSync as
|
|
35446
|
-
mkdirSync as
|
|
36224
|
+
writeFileSync as writeFileSync16,
|
|
36225
|
+
mkdirSync as mkdirSync8,
|
|
35447
36226
|
mkdtempSync as mkdtempSync5,
|
|
35448
36227
|
renameSync as renameSync6,
|
|
35449
36228
|
existsSync as existsSync13,
|
|
35450
|
-
lstatSync as
|
|
36229
|
+
lstatSync as lstatSync4,
|
|
35451
36230
|
realpathSync as realpathSync2,
|
|
35452
36231
|
rmSync as rmSync10,
|
|
35453
|
-
readdirSync as
|
|
36232
|
+
readdirSync as readdirSync4
|
|
35454
36233
|
} from "fs";
|
|
35455
|
-
import { join as
|
|
36234
|
+
import { join as join29, dirname as dirname8, isAbsolute as isAbsolute4, resolve as pathResolve } from "path";
|
|
35456
36235
|
import { createHash as createHash10 } from "crypto";
|
|
35457
36236
|
import { homedir as homedir14, hostname as osHostname } from "os";
|
|
35458
36237
|
import { execFile as execFile3, execFileSync as execFileSync2, spawnSync as spawnSync8 } from "child_process";
|
|
@@ -35899,10 +36678,10 @@ async function syncFounderVerdicts({
|
|
|
35899
36678
|
|
|
35900
36679
|
// bin/jpi-claim.js
|
|
35901
36680
|
init_founder_note_sync();
|
|
35902
|
-
var TERMINALHIRE_DIR11 = process.env.TERMINALHIRE_DIR ||
|
|
35903
|
-
var INDEX_CACHE_FILE2 =
|
|
35904
|
-
var CLAIM_PUSH_MARKER =
|
|
35905
|
-
var REPO_CONTINUITY_NUDGE_MARKER =
|
|
36681
|
+
var TERMINALHIRE_DIR11 = process.env.TERMINALHIRE_DIR || join29(homedir14(), ".terminalhire");
|
|
36682
|
+
var INDEX_CACHE_FILE2 = join29(TERMINALHIRE_DIR11, "index-cache.json");
|
|
36683
|
+
var CLAIM_PUSH_MARKER = join29(TERMINALHIRE_DIR11, "claim-push.json");
|
|
36684
|
+
var REPO_CONTINUITY_NUDGE_MARKER = join29(TERMINALHIRE_DIR11, "repo-continuity-nudged.json");
|
|
35906
36685
|
function readNudgedClaimIds() {
|
|
35907
36686
|
try {
|
|
35908
36687
|
const raw = JSON.parse(readFileSync14(REPO_CONTINUITY_NUDGE_MARKER, "utf8"));
|
|
@@ -35916,7 +36695,7 @@ function markClaimNudged(id) {
|
|
|
35916
36695
|
const ids2 = readNudgedClaimIds();
|
|
35917
36696
|
ids2.add(id);
|
|
35918
36697
|
ensureStateDir(TERMINALHIRE_DIR11);
|
|
35919
|
-
|
|
36698
|
+
writeFileSync16(REPO_CONTINUITY_NUDGE_MARKER, JSON.stringify({ claimIds: [...ids2] }), "utf8");
|
|
35920
36699
|
} catch {
|
|
35921
36700
|
}
|
|
35922
36701
|
}
|
|
@@ -36033,7 +36812,7 @@ async function sh(cmd, args, opts = {}) {
|
|
|
36033
36812
|
async function confirm(question) {
|
|
36034
36813
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
36035
36814
|
try {
|
|
36036
|
-
const ans = await new Promise((
|
|
36815
|
+
const ans = await new Promise((resolve5) => rl.question(question, resolve5));
|
|
36037
36816
|
return /^y(es)?$/i.test(String(ans).trim());
|
|
36038
36817
|
} finally {
|
|
36039
36818
|
rl.close();
|
|
@@ -36064,7 +36843,7 @@ async function confirmSubmit(flags, question, { unattendedAllowed = true, refusa
|
|
|
36064
36843
|
async function ask(question) {
|
|
36065
36844
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
36066
36845
|
try {
|
|
36067
|
-
const ans = await new Promise((
|
|
36846
|
+
const ans = await new Promise((resolve5) => rl.question(question, resolve5));
|
|
36068
36847
|
return String(ans).trim();
|
|
36069
36848
|
} finally {
|
|
36070
36849
|
rl.close();
|
|
@@ -36178,7 +36957,7 @@ function inferSubmitClaim({
|
|
|
36178
36957
|
}
|
|
36179
36958
|
return { candidates: [] };
|
|
36180
36959
|
}
|
|
36181
|
-
function
|
|
36960
|
+
function canonicalPath2(p) {
|
|
36182
36961
|
if (typeof p !== "string" || p === "") return p;
|
|
36183
36962
|
const resolved = pathResolve(p);
|
|
36184
36963
|
try {
|
|
@@ -38249,7 +39028,7 @@ async function cmdAttach(id, worktree, branch) {
|
|
|
38249
39028
|
function workDirFor(repoFullName, issueNumber) {
|
|
38250
39029
|
const [owner, repo] = String(repoFullName).split("/");
|
|
38251
39030
|
const suffix = issueNumber ? `-${issueNumber}` : "";
|
|
38252
|
-
return
|
|
39031
|
+
return join29(homedir14(), "terminalhire", "work", `${owner}-${repo}${suffix}`);
|
|
38253
39032
|
}
|
|
38254
39033
|
function startBranchFor(repoFullName, issueNumber) {
|
|
38255
39034
|
const repo = String(repoFullName).split("/")[1] || "claim";
|
|
@@ -38505,7 +39284,7 @@ terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to y
|
|
|
38505
39284
|
);
|
|
38506
39285
|
process.exit(1);
|
|
38507
39286
|
}
|
|
38508
|
-
|
|
39287
|
+
mkdirSync8(join29(homedir14(), "terminalhire", "work"), { recursive: true });
|
|
38509
39288
|
const { createProgress: createProgress2, parseGitProgress: parseGitProgress2, splitProgressChunk: splitProgressChunk2, shStream: shStream2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
38510
39289
|
const progress = createProgress2();
|
|
38511
39290
|
let forkFullName;
|
|
@@ -38633,7 +39412,7 @@ function founderPostingIdOf(claim) {
|
|
|
38633
39412
|
}
|
|
38634
39413
|
function sliceWorkDirFor(claimLocalId) {
|
|
38635
39414
|
const safe = String(claimLocalId).replace(/[^A-Za-z0-9._-]/g, "-");
|
|
38636
|
-
return
|
|
39415
|
+
return join29(homedir14(), "terminalhire", "work", `slice-${safe}`);
|
|
38637
39416
|
}
|
|
38638
39417
|
function assertNoBooleanPath(dest, flagName) {
|
|
38639
39418
|
const last = String(dest).split(/[\\/]/).filter(Boolean).pop();
|
|
@@ -38646,7 +39425,7 @@ function assertNoBooleanPath(dest, flagName) {
|
|
|
38646
39425
|
}
|
|
38647
39426
|
function resolveDeliveryDir(flags, claimLocalId, { existsFn, readdirFn } = {}) {
|
|
38648
39427
|
const exists = existsFn ?? existsSync13;
|
|
38649
|
-
const readdir3 = readdirFn ??
|
|
39428
|
+
const readdir3 = readdirFn ?? readdirSync4;
|
|
38650
39429
|
let probing = null;
|
|
38651
39430
|
try {
|
|
38652
39431
|
const base = flags?.dir ? assertNoBooleanPath(pathResolve(String(flags.dir)), "dir") : sliceWorkDirFor(claimLocalId);
|
|
@@ -38758,9 +39537,9 @@ function writeSliceFiles(destDir, files) {
|
|
|
38758
39537
|
const unavailable = [];
|
|
38759
39538
|
for (const f of files) {
|
|
38760
39539
|
if (typeof f.content === "string") {
|
|
38761
|
-
const abs =
|
|
38762
|
-
|
|
38763
|
-
|
|
39540
|
+
const abs = join29(destDir, f.path);
|
|
39541
|
+
mkdirSync8(dirname8(abs), { recursive: true });
|
|
39542
|
+
writeFileSync16(abs, f.content, "utf8");
|
|
38764
39543
|
written.push(f.path);
|
|
38765
39544
|
} else {
|
|
38766
39545
|
unavailable.push({ path: f.path, reason: f.unavailableReason || "(no reason given)" });
|
|
@@ -38794,7 +39573,7 @@ function writeDeliveredBrief(destDir, spec) {
|
|
|
38794
39573
|
function ensureExcludedPackDir(destDir) {
|
|
38795
39574
|
let occupant = null;
|
|
38796
39575
|
try {
|
|
38797
|
-
occupant =
|
|
39576
|
+
occupant = lstatSync4(join29(destDir, BRIEF_DIR));
|
|
38798
39577
|
} catch (err) {
|
|
38799
39578
|
if (err?.code !== "ENOENT") {
|
|
38800
39579
|
return {
|
|
@@ -38809,12 +39588,12 @@ function ensureExcludedPackDir(destDir) {
|
|
|
38809
39588
|
reason: `${BRIEF_DIR}/ already exists in the delivered tree, and excluding it would hide that content from your patch`
|
|
38810
39589
|
};
|
|
38811
39590
|
}
|
|
38812
|
-
const excludeFile =
|
|
39591
|
+
const excludeFile = join29(destDir, ".git", "info", "exclude");
|
|
38813
39592
|
try {
|
|
38814
39593
|
const existing = existsSync13(excludeFile) ? readFileSync14(excludeFile, "utf8") : "";
|
|
38815
39594
|
if (!existing.split("\n").includes(BRIEF_EXCLUDE_LINE)) {
|
|
38816
|
-
|
|
38817
|
-
|
|
39595
|
+
mkdirSync8(dirname8(excludeFile), { recursive: true });
|
|
39596
|
+
writeFileSync16(
|
|
38818
39597
|
excludeFile,
|
|
38819
39598
|
`${existing}${existing === "" || existing.endsWith("\n") ? "" : "\n"}${BRIEF_EXCLUDE_LINE}
|
|
38820
39599
|
`,
|
|
@@ -38828,9 +39607,9 @@ function ensureExcludedPackDir(destDir) {
|
|
|
38828
39607
|
}
|
|
38829
39608
|
function writePackFile(destDir, relPath, content, what) {
|
|
38830
39609
|
try {
|
|
38831
|
-
const abs =
|
|
38832
|
-
|
|
38833
|
-
|
|
39610
|
+
const abs = join29(destDir, relPath);
|
|
39611
|
+
mkdirSync8(dirname8(abs), { recursive: true });
|
|
39612
|
+
writeFileSync16(abs, content, { encoding: "utf8", flag: "wx" });
|
|
38834
39613
|
} catch (err) {
|
|
38835
39614
|
return { written: false, reason: `the ${what} could not be written (${err.message})` };
|
|
38836
39615
|
}
|
|
@@ -39245,7 +40024,7 @@ async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRep
|
|
|
39245
40024
|
if (resolvedDir.suffixed) {
|
|
39246
40025
|
console.log(`terminalhire claim: the usual directory has content \u2014 using ${dest}`);
|
|
39247
40026
|
}
|
|
39248
|
-
|
|
40027
|
+
mkdirSync8(dest, { recursive: true });
|
|
39249
40028
|
const branch = `claim/${String(claim.id).replace(/[^A-Za-z0-9._-]/g, "-")}`;
|
|
39250
40029
|
let engine;
|
|
39251
40030
|
try {
|
|
@@ -39427,7 +40206,7 @@ async function attemptSliceDelivery(id, flags = {}) {
|
|
|
39427
40206
|
if (resolvedDir.suffixed) {
|
|
39428
40207
|
console.log(`terminalhire claim: the usual directory has content \u2014 using ${finalDest}`);
|
|
39429
40208
|
}
|
|
39430
|
-
|
|
40209
|
+
mkdirSync8(dirname8(finalDest), { recursive: true });
|
|
39431
40210
|
let dest = mkdtempSync5(`${finalDest}.tmp-`);
|
|
39432
40211
|
const { written, unavailable } = writeSliceFiles(dest, body.files);
|
|
39433
40212
|
const branch = `claim/${String(claim.id).replace(/[^A-Za-z0-9._-]/g, "-")}`;
|
|
@@ -39750,7 +40529,7 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
39750
40529
|
cwdToplevel: cwdToplevel2,
|
|
39751
40530
|
cwd: pathResolve(dir),
|
|
39752
40531
|
branch,
|
|
39753
|
-
canonicalize:
|
|
40532
|
+
canonicalize: canonicalPath2
|
|
39754
40533
|
});
|
|
39755
40534
|
if (inferred.claim) {
|
|
39756
40535
|
id = inferred.claim.id;
|
|
@@ -39968,7 +40747,7 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
39968
40747
|
const head = `${ghUser}:${claim.branch}`;
|
|
39969
40748
|
const title = flags.title || claim.title;
|
|
39970
40749
|
const noBody = Boolean(flags["no-body"]);
|
|
39971
|
-
const prBodyPath =
|
|
40750
|
+
const prBodyPath = join29(wt, "PR-BODY.md");
|
|
39972
40751
|
const bodySource = pickBodySource({
|
|
39973
40752
|
bodyFileFlag: flags["body-file"],
|
|
39974
40753
|
noBody,
|
|
@@ -40208,7 +40987,7 @@ function readClaimPushMarker() {
|
|
|
40208
40987
|
}
|
|
40209
40988
|
function writeClaimPushMarker(marker) {
|
|
40210
40989
|
ensureStateDir(TERMINALHIRE_DIR11);
|
|
40211
|
-
|
|
40990
|
+
writeFileSync16(CLAIM_PUSH_MARKER, JSON.stringify(marker, null, 2) + "\n", "utf8");
|
|
40212
40991
|
}
|
|
40213
40992
|
function clearClaimPushMarker() {
|
|
40214
40993
|
try {
|