@tablation/crew 0.129.0 → 0.130.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +190 -68
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1259,6 +1259,14 @@ var claudePair = {
|
|
|
1259
1259
|
removePair: (o) => removeClaudeMcp({ bin: o.bin ?? "claude", dryRun: o.dryRun })
|
|
1260
1260
|
};
|
|
1261
1261
|
var inferenceBaseUrl = (baseUrl, workspaceId) => `${baseUrl.replace(/\/+$/, "")}/api/workspaces/${workspaceId}/inference`;
|
|
1262
|
+
function inferenceHeaders(inf) {
|
|
1263
|
+
const clean = (v) => v.replace(/[\r\n]+/g, " ").trim();
|
|
1264
|
+
const lines2 = [
|
|
1265
|
+
...inf.ticket ? [`X-Crew-Ticket: ${clean(inf.ticket)}`] : [],
|
|
1266
|
+
...inf.runId ? [`X-Crew-Run: ${clean(inf.runId)}`] : []
|
|
1267
|
+
];
|
|
1268
|
+
return lines2.length > 0 ? lines2.join("\n") : void 0;
|
|
1269
|
+
}
|
|
1262
1270
|
function workspaceSettings(baseUrl) {
|
|
1263
1271
|
const blank = BILLING_VARS_TO_UNSET.filter((v) => v !== "ANTHROPIC_AUTH_TOKEN");
|
|
1264
1272
|
return JSON.stringify({ env: { ...Object.fromEntries(blank.map((v) => [v, ""])), ANTHROPIC_BASE_URL: baseUrl } });
|
|
@@ -1362,7 +1370,11 @@ var claudeAdapter = {
|
|
|
1362
1370
|
// variables are already unset at exec (`unsetEnv`), and a spec-level `env:` entry must not
|
|
1363
1371
|
// smuggle one back in.
|
|
1364
1372
|
...spec.auth === "workspace" ? withoutProviderVars(spec.env) : spec.env,
|
|
1365
|
-
...spec.auth === "workspace" && spec.inference ? {
|
|
1373
|
+
...spec.auth === "workspace" && spec.inference ? {
|
|
1374
|
+
ANTHROPIC_BASE_URL: spec.inference.baseUrl,
|
|
1375
|
+
ANTHROPIC_AUTH_TOKEN: spec.inference.token,
|
|
1376
|
+
...inferenceHeaders(spec.inference) ? { ANTHROPIC_CUSTOM_HEADERS: inferenceHeaders(spec.inference) } : {}
|
|
1377
|
+
} : {}
|
|
1366
1378
|
};
|
|
1367
1379
|
},
|
|
1368
1380
|
provider: () => "anthropic",
|
|
@@ -3210,7 +3222,7 @@ function parseRepoConfig(text2, file) {
|
|
|
3210
3222
|
if (!MATCHES.includes(vmatch)) {
|
|
3211
3223
|
throw new RepoConfigError(`${file}: release.verify.match must be commit|version (got "${vmatch}")`);
|
|
3212
3224
|
}
|
|
3213
|
-
const
|
|
3225
|
+
const num2 = (v, key, dflt) => {
|
|
3214
3226
|
if (v === void 0 || v === null) return dflt;
|
|
3215
3227
|
const n = Number(v);
|
|
3216
3228
|
if (!Number.isFinite(n) || n <= 0) {
|
|
@@ -3220,8 +3232,8 @@ function parseRepoConfig(text2, file) {
|
|
|
3220
3232
|
};
|
|
3221
3233
|
const verify = {
|
|
3222
3234
|
match: vmatch,
|
|
3223
|
-
timeoutSeconds:
|
|
3224
|
-
intervalSeconds:
|
|
3235
|
+
timeoutSeconds: num2(raw.release?.verify?.timeoutSeconds, "timeoutSeconds", 600),
|
|
3236
|
+
intervalSeconds: num2(raw.release?.verify?.intervalSeconds, "intervalSeconds", 15)
|
|
3225
3237
|
};
|
|
3226
3238
|
if (verify.intervalSeconds > verify.timeoutSeconds) {
|
|
3227
3239
|
throw new RepoConfigError(
|
|
@@ -3794,7 +3806,7 @@ function parseReleaseOverride(raw, where, file) {
|
|
|
3794
3806
|
if (versioning !== void 0 && !VERSIONINGS.includes(versioning)) {
|
|
3795
3807
|
throw new ConfigError(`${file}: ${where}.release.versioning must be auto|none (got "${raw.versioning}")`);
|
|
3796
3808
|
}
|
|
3797
|
-
const
|
|
3809
|
+
const num2 = (v, key) => {
|
|
3798
3810
|
if (v === void 0 || v === null) return void 0;
|
|
3799
3811
|
const n = Number(v);
|
|
3800
3812
|
if (!Number.isFinite(n) || n <= 0) {
|
|
@@ -3802,8 +3814,8 @@ function parseReleaseOverride(raw, where, file) {
|
|
|
3802
3814
|
}
|
|
3803
3815
|
return n;
|
|
3804
3816
|
};
|
|
3805
|
-
const timeoutSeconds =
|
|
3806
|
-
const intervalSeconds =
|
|
3817
|
+
const timeoutSeconds = num2(raw.verify?.timeoutSeconds, "timeoutSeconds");
|
|
3818
|
+
const intervalSeconds = num2(raw.verify?.intervalSeconds, "intervalSeconds");
|
|
3807
3819
|
const isolateMax = parseIsolateMax(
|
|
3808
3820
|
raw.isolateFailures?.maxCandidates,
|
|
3809
3821
|
`${where}.release.isolateFailures`,
|
|
@@ -5165,9 +5177,22 @@ function planDaemonControl(crewHome, hostShip, hasSystemd) {
|
|
|
5165
5177
|
}
|
|
5166
5178
|
return { mechanism: "none", startCommand: null, stopCommand: null, statusCommand: null };
|
|
5167
5179
|
}
|
|
5180
|
+
function withUserBusEnv(env = process.env, opts = {}) {
|
|
5181
|
+
const platform2 = opts.platform ?? process.platform;
|
|
5182
|
+
const uid = opts.uid === void 0 ? process.getuid?.() ?? null : opts.uid;
|
|
5183
|
+
if (platform2 !== "linux" || uid === null) return env;
|
|
5184
|
+
const dir = `/run/user/${uid}`;
|
|
5185
|
+
if (env.XDG_RUNTIME_DIR && env.DBUS_SESSION_BUS_ADDRESS) return env;
|
|
5186
|
+
if (!(opts.exists ?? existsSync15)(dir)) return env;
|
|
5187
|
+
return {
|
|
5188
|
+
...env,
|
|
5189
|
+
XDG_RUNTIME_DIR: env.XDG_RUNTIME_DIR || dir,
|
|
5190
|
+
DBUS_SESSION_BUS_ADDRESS: env.DBUS_SESSION_BUS_ADDRESS || `unix:path=${env.XDG_RUNTIME_DIR || dir}/bus`
|
|
5191
|
+
};
|
|
5192
|
+
}
|
|
5168
5193
|
function runAll(commands, cwd, log) {
|
|
5169
5194
|
for (const [bin, ...args] of commands) {
|
|
5170
|
-
const res = spawnSync5(bin, args, { cwd, encoding: "utf8" });
|
|
5195
|
+
const res = spawnSync5(bin, args, { cwd, encoding: "utf8", env: withUserBusEnv() });
|
|
5171
5196
|
if (res.error || res.status !== 0) {
|
|
5172
5197
|
log.warn(`${bin} ${args.join(" ")} exited ${res.status ?? "error"}`, {
|
|
5173
5198
|
output: `${res.stdout ?? ""}${res.stderr ?? ""}${res.error ? String(res.error) : ""}`
|
|
@@ -5393,7 +5418,7 @@ function removeCrontabLine(marker) {
|
|
|
5393
5418
|
}
|
|
5394
5419
|
function detectSystemd() {
|
|
5395
5420
|
if (process.platform !== "linux") return false;
|
|
5396
|
-
const res = spawnSync5("systemctl", ["--user", "status"], { encoding: "utf8" });
|
|
5421
|
+
const res = spawnSync5("systemctl", ["--user", "status"], { encoding: "utf8", env: withUserBusEnv() });
|
|
5397
5422
|
return !res.error;
|
|
5398
5423
|
}
|
|
5399
5424
|
|
|
@@ -5483,20 +5508,20 @@ function branchForIssue(cwd, issueKey, templates = { name: "issue-{number}" }, r
|
|
|
5483
5508
|
return null;
|
|
5484
5509
|
}
|
|
5485
5510
|
function branchCandidates(issueKey, templates, render2) {
|
|
5486
|
-
const
|
|
5487
|
-
const unpadded = String(Number.parseInt(
|
|
5511
|
+
const num2 = issueKey.replace(/^\D+/, "");
|
|
5512
|
+
const unpadded = String(Number.parseInt(num2, 10));
|
|
5488
5513
|
const candidates2 = [];
|
|
5489
5514
|
for (const template of [templates.name, templates.push].filter(Boolean)) {
|
|
5490
5515
|
const exact = render2(template);
|
|
5491
5516
|
candidates2.push(exact);
|
|
5492
|
-
if (
|
|
5517
|
+
if (num2 !== unpadded) candidates2.push(exact.replace(num2, unpadded));
|
|
5493
5518
|
if (template.includes("{slug}")) {
|
|
5494
5519
|
const prefix = render2(template.slice(0, template.indexOf("{slug}")));
|
|
5495
5520
|
if (prefix) candidates2.push(`${prefix}*`);
|
|
5496
5521
|
}
|
|
5497
5522
|
}
|
|
5498
|
-
candidates2.push(`issue-${
|
|
5499
|
-
if (
|
|
5523
|
+
candidates2.push(`issue-${num2}`);
|
|
5524
|
+
if (num2 !== unpadded) candidates2.push(`issue-${unpadded}`);
|
|
5500
5525
|
return [...new Set(candidates2)];
|
|
5501
5526
|
}
|
|
5502
5527
|
function remoteBranchForIssue(cwd, remote, issueKey, templates = { name: "issue-{number}" }, render2 = (t) => t.replace(/\{number\}/g, issueKey.replace(/^\D+/, ""))) {
|
|
@@ -5567,8 +5592,8 @@ function deleteRemoteBranch(cwd, remote, branch) {
|
|
|
5567
5592
|
if (!remoteBranchExists(cwd, remote, branch)) return true;
|
|
5568
5593
|
return gitOk(cwd, ["push", remote, "--delete", branch]) !== null;
|
|
5569
5594
|
}
|
|
5570
|
-
function worktreeForNumber(cwd,
|
|
5571
|
-
const re = new RegExp(`(?:^|-)${
|
|
5595
|
+
function worktreeForNumber(cwd, num2) {
|
|
5596
|
+
const re = new RegExp(`(?:^|-)${num2}$`);
|
|
5572
5597
|
return worktrees(cwd).find((w) => re.test(basename5(w.path))) ?? null;
|
|
5573
5598
|
}
|
|
5574
5599
|
var commitBodies = (cwd, range) => gitOk(cwd, ["log", range, "--format=%B"]) ?? "";
|
|
@@ -6462,7 +6487,7 @@ import { readFileSync as readFileSync26, existsSync as existsSync25, statSync as
|
|
|
6462
6487
|
import { StringDecoder } from "string_decoder";
|
|
6463
6488
|
import { finished } from "stream/promises";
|
|
6464
6489
|
import { join as join29, dirname as dirname14, basename as basename9 } from "path";
|
|
6465
|
-
import { createHash as createHash5 } from "crypto";
|
|
6490
|
+
import { createHash as createHash5, randomUUID } from "crypto";
|
|
6466
6491
|
|
|
6467
6492
|
// src/state.ts
|
|
6468
6493
|
import { readFileSync as readFileSync19, writeFileSync as writeFileSync14, existsSync as existsSync18, mkdirSync as mkdirSync13, rmSync as rmSync10, statSync as statSync7, readdirSync as readdirSync5 } from "fs";
|
|
@@ -9593,8 +9618,8 @@ async function noteTakeover(tracker, result, seat, ships, myShipName, continuing
|
|
|
9593
9618
|
}
|
|
9594
9619
|
}
|
|
9595
9620
|
function originBranchSha(dir, t, rev) {
|
|
9596
|
-
const
|
|
9597
|
-
const names = [...new Set([(t.issue_tag ?? "").toLowerCase(), `issue-${
|
|
9621
|
+
const num2 = (t.issue_tag ?? t.issue_id).replace(/^\D+/, "");
|
|
9622
|
+
const names = [...new Set([(t.issue_tag ?? "").toLowerCase(), `issue-${num2}`].filter(Boolean))];
|
|
9598
9623
|
for (const n of names) {
|
|
9599
9624
|
const sha = rev(dir, `origin/${n}`);
|
|
9600
9625
|
if (sha) return sha.slice(0, 7);
|
|
@@ -9656,8 +9681,8 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
|
|
|
9656
9681
|
unlock();
|
|
9657
9682
|
}
|
|
9658
9683
|
}
|
|
9659
|
-
const
|
|
9660
|
-
const existing = worktreeForNumber(dir,
|
|
9684
|
+
const num2 = t.issue_id.replace(/^\D+/, "");
|
|
9685
|
+
const existing = worktreeForNumber(dir, num2);
|
|
9661
9686
|
const located = locateBranchForTicket(dir, cfg, t, role);
|
|
9662
9687
|
const baseRef = hasRemote ? `${remote}/${base}` : base;
|
|
9663
9688
|
if (existing && !existing.prunable && existsSync21(existing.path)) {
|
|
@@ -10154,6 +10179,49 @@ async function guardRunStrays(route2, ship, tracker, role, workingId, since2, lo
|
|
|
10154
10179
|
}
|
|
10155
10180
|
|
|
10156
10181
|
// src/agent-log.ts
|
|
10182
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : Number(v) || 0;
|
|
10183
|
+
function sumInferenceRows(rows) {
|
|
10184
|
+
const t = { calls: 0, tokensIn: 0, tokensOut: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0 };
|
|
10185
|
+
for (const r of rows) {
|
|
10186
|
+
t.calls++;
|
|
10187
|
+
t.tokensIn += num(r.input_tokens);
|
|
10188
|
+
t.tokensOut += num(r.output_tokens);
|
|
10189
|
+
t.cacheReadTokens += num(r.cache_read_tokens);
|
|
10190
|
+
t.cacheWriteTokens += num(r.cache_write_tokens);
|
|
10191
|
+
t.costUsd += num(r.cost_usd);
|
|
10192
|
+
}
|
|
10193
|
+
return t;
|
|
10194
|
+
}
|
|
10195
|
+
var INFERENCE_PAGE = 1e3;
|
|
10196
|
+
async function listInferenceRows(target, modelId, runId) {
|
|
10197
|
+
const filters = encodeURIComponent(JSON.stringify({
|
|
10198
|
+
combinator: "AND",
|
|
10199
|
+
conditions: [
|
|
10200
|
+
{ columnName: "run_id", operator: "EQ", value: runId },
|
|
10201
|
+
{ columnName: "client", operator: "EQ", value: "inference" }
|
|
10202
|
+
]
|
|
10203
|
+
}));
|
|
10204
|
+
const out = [];
|
|
10205
|
+
for (let offset = 0; ; offset += INFERENCE_PAGE) {
|
|
10206
|
+
const page = await request(
|
|
10207
|
+
target,
|
|
10208
|
+
"GET",
|
|
10209
|
+
`/data-models/${modelId}/records?limit=${INFERENCE_PAGE}&offset=${offset}&filters=${filters}`
|
|
10210
|
+
);
|
|
10211
|
+
out.push(...page);
|
|
10212
|
+
if (page.length < INFERENCE_PAGE) return out;
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
async function inferenceTotalsFor(target, runId) {
|
|
10216
|
+
const model = await request(
|
|
10217
|
+
target,
|
|
10218
|
+
"GET",
|
|
10219
|
+
`/data-models/agent_log?workspaceId=${encodeURIComponent(target.workspaceId)}`
|
|
10220
|
+
);
|
|
10221
|
+
if (!(await columnsOf(target, model.id)).has("run_id")) return void 0;
|
|
10222
|
+
const rows = await listInferenceRows(target, model.id, runId);
|
|
10223
|
+
return rows.length > 0 ? sumInferenceRows(rows) : void 0;
|
|
10224
|
+
}
|
|
10157
10225
|
var AgentLogUnreachableError = class extends Error {
|
|
10158
10226
|
};
|
|
10159
10227
|
var AgentLogHttpError = class extends Error {
|
|
@@ -10318,19 +10386,20 @@ async function reportAgentRun(target, run3) {
|
|
|
10318
10386
|
promptVersion: run3.promptVersion,
|
|
10319
10387
|
promptSha: run3.promptSha
|
|
10320
10388
|
};
|
|
10389
|
+
const { runId, ...legacyBody } = { ...body, runId: run3.runId };
|
|
10321
10390
|
const workItem = target.tickets && ticketRecordId ? { workItemTable: target.tickets.issuesModelId, workItemRecordId: ticketRecordId } : {};
|
|
10322
10391
|
let entry;
|
|
10323
10392
|
try {
|
|
10324
|
-
entry = await post(target, "/agents/log", { ...body, ...workItem, harness: run3.harness, provider: run3.provider });
|
|
10393
|
+
entry = await post(target, "/agents/log", { ...body, ...runId ? { runId } : {}, ...workItem, harness: run3.harness, provider: run3.provider });
|
|
10325
10394
|
} catch (e) {
|
|
10326
10395
|
if (!(e instanceof AgentLogHttpError)) throw new AgentLogUnreachableError(e.message);
|
|
10327
10396
|
if (e.status >= 500) throw new AgentLogUnreachableError(e.message);
|
|
10328
|
-
if (e.status !== 400 || !run3.harness && !run3.provider && !ticketRecordId) {
|
|
10397
|
+
if (e.status !== 400 || !run3.harness && !run3.provider && !ticketRecordId && !runId) {
|
|
10329
10398
|
e.row = true;
|
|
10330
10399
|
throw e;
|
|
10331
10400
|
}
|
|
10332
10401
|
try {
|
|
10333
|
-
entry = await post(target, "/agents/log",
|
|
10402
|
+
entry = await post(target, "/agents/log", legacyBody);
|
|
10334
10403
|
} catch (e2) {
|
|
10335
10404
|
if (e2 instanceof AgentLogHttpError && e2.status < 500) e2.row = true;
|
|
10336
10405
|
throw e2;
|
|
@@ -10349,10 +10418,21 @@ async function reportAgentRun(target, run3) {
|
|
|
10349
10418
|
const bounces = await countAcceptanceBounces(target, target.tickets.issuesModelId, ticketRecordId);
|
|
10350
10419
|
if (bounces !== null) await writeHandoffBounces(target, entry.id, bounces);
|
|
10351
10420
|
}
|
|
10352
|
-
|
|
10421
|
+
let counterWarning;
|
|
10422
|
+
let counted = run3;
|
|
10423
|
+
if (runId && target.tickets && ticketRecordId) {
|
|
10424
|
+
try {
|
|
10425
|
+
const totals = await inferenceTotalsFor(target, runId);
|
|
10426
|
+
if (totals) counted = { ...run3, tokensIn: totals.tokensIn, tokensOut: totals.tokensOut, cacheReadTokens: totals.cacheReadTokens, cacheWriteTokens: totals.cacheWriteTokens, costUsd: totals.costUsd };
|
|
10427
|
+
else counted = { ...run3, ...run3.harnessTotals };
|
|
10428
|
+
} catch (e) {
|
|
10429
|
+
counterWarning = `could not sum run ${runId}'s inference rows for ${run3.ticketReference}: ${e.message}`;
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
const counters = target.tickets && ticketRecordId ? await addRunToTicket(target, target.tickets, ticketRecordId, counted) : void 0;
|
|
10353
10433
|
return {
|
|
10354
10434
|
agentLogId: entry.id,
|
|
10355
|
-
...counters?.warning ? { counterWarning: counters.
|
|
10435
|
+
...counters?.warning || counterWarning ? { counterWarning: [counterWarning, counters?.warning].filter(Boolean).join("; ") } : {},
|
|
10356
10436
|
...counters?.retry ? { counterRetry: counters.retry } : {}
|
|
10357
10437
|
};
|
|
10358
10438
|
}
|
|
@@ -11928,6 +12008,7 @@ function planAgentRun(o) {
|
|
|
11928
12008
|
const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
|
|
11929
12009
|
const { model } = seat;
|
|
11930
12010
|
let toolchain = seat.toolchain;
|
|
12011
|
+
let workspaceRunId;
|
|
11931
12012
|
if (toolchain.auth === "workspace") {
|
|
11932
12013
|
if (!o.apiKey || o.seatKind === "ship") {
|
|
11933
12014
|
throw new WorkspaceAuthRefused(`${o.role}: toolchain ${toolchain.name} has auth: workspace, which needs the seat's own agent key \u2014 this seat would fall back to the ship key (run \`crew agents sync\`)`);
|
|
@@ -11935,7 +12016,8 @@ function planAgentRun(o) {
|
|
|
11935
12016
|
if (!o.route.resolved) {
|
|
11936
12017
|
throw new WorkspaceAuthRefused(`${o.role}: toolchain ${toolchain.name} has auth: workspace, but route ${o.route.route} is not connected (run \`crew connect\`)`);
|
|
11937
12018
|
}
|
|
11938
|
-
|
|
12019
|
+
workspaceRunId = o.runId ?? randomUUID();
|
|
12020
|
+
toolchain = { ...toolchain, inference: { baseUrl: inferenceBaseUrl(o.route.baseUrl, o.route.resolved.workspaceId), token: o.apiKey, ticket: o.ticket, runId: workspaceRunId } };
|
|
11939
12021
|
}
|
|
11940
12022
|
const adapter = adapterFor(toolchain.harness);
|
|
11941
12023
|
const code = codeBridgeFor(o, adapter.harness);
|
|
@@ -12032,7 +12114,8 @@ function planAgentRun(o) {
|
|
|
12032
12114
|
routeName: o.route.route,
|
|
12033
12115
|
agentLog,
|
|
12034
12116
|
promptVersion: o.route.resolved?.agentPersonas?.[o.role]?.historyId,
|
|
12035
|
-
promptSha
|
|
12117
|
+
promptSha,
|
|
12118
|
+
...workspaceRunId ? { workspaceRunId } : {}
|
|
12036
12119
|
};
|
|
12037
12120
|
}
|
|
12038
12121
|
function describePlan(p) {
|
|
@@ -12086,7 +12169,8 @@ function identityOf(plan, adapterHarness) {
|
|
|
12086
12169
|
streamPath: plan.streamPath,
|
|
12087
12170
|
agentLog: plan.agentLog ? withoutKey(plan.agentLog) : void 0,
|
|
12088
12171
|
promptVersion: plan.promptVersion,
|
|
12089
|
-
promptSha: plan.promptSha
|
|
12172
|
+
promptSha: plan.promptSha,
|
|
12173
|
+
...plan.workspaceRunId ? { workspaceRunId: plan.workspaceRunId } : {}
|
|
12090
12174
|
};
|
|
12091
12175
|
}
|
|
12092
12176
|
async function killForDeadline(kill, pid, emit, role, ticket, spawnedAt, limitMinutes) {
|
|
@@ -12393,11 +12477,22 @@ async function finishRun(id, obs, emit, startedAt, end, agentLogKey, hintMoved)
|
|
|
12393
12477
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12394
12478
|
cycles: obs.cycles,
|
|
12395
12479
|
model: id.model,
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12480
|
+
// CREW-2980: under `auth: workspace` the endpoint's rows are the accounting; the run row
|
|
12481
|
+
// keeps turns only, and `reportAgentRun` sums those rows into the ticket counters.
|
|
12482
|
+
...id.workspaceRunId ? { runId: id.workspaceRunId } : {
|
|
12483
|
+
tokensIn: result?.inputTokens,
|
|
12484
|
+
tokensOut: result?.outputTokens,
|
|
12485
|
+
cacheReadTokens: result?.cacheReadTokens,
|
|
12486
|
+
cacheWriteTokens: result?.cacheWriteTokens,
|
|
12487
|
+
costUsd: result?.costUsd
|
|
12488
|
+
},
|
|
12489
|
+
...id.workspaceRunId ? { harnessTotals: {
|
|
12490
|
+
tokensIn: result?.inputTokens,
|
|
12491
|
+
tokensOut: result?.outputTokens,
|
|
12492
|
+
cacheReadTokens: result?.cacheReadTokens,
|
|
12493
|
+
cacheWriteTokens: result?.cacheWriteTokens,
|
|
12494
|
+
costUsd: result?.costUsd
|
|
12495
|
+
} } : {},
|
|
12401
12496
|
// Both come from a finished run: without the result line (interrupted) neither is trustworthy.
|
|
12402
12497
|
...result?.numTurns !== void 0 ? { numTurns: result.numTurns } : {},
|
|
12403
12498
|
...result?.numTurns !== void 0 && obs.prefixTokens !== void 0 ? { prefixTokens: obs.prefixTokens } : {},
|
|
@@ -12524,7 +12619,8 @@ function spawnAgent(plan, emit, hintMoved) {
|
|
|
12524
12619
|
eventsPath: plan.eventsPath,
|
|
12525
12620
|
agentLog: id.agentLog,
|
|
12526
12621
|
promptVersion: plan.promptVersion,
|
|
12527
|
-
promptSha: plan.promptSha
|
|
12622
|
+
promptSha: plan.promptSha,
|
|
12623
|
+
...plan.workspaceRunId ? { workspaceRunId: plan.workspaceRunId } : {}
|
|
12528
12624
|
};
|
|
12529
12625
|
try {
|
|
12530
12626
|
writeFileSync19(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
|
|
@@ -12779,14 +12875,14 @@ function describeRun(run3, route2, seatIds, worktreeOf = (d, n) => {
|
|
|
12779
12875
|
}) {
|
|
12780
12876
|
const ownRoute = run3.route === routeSlug(route2.route);
|
|
12781
12877
|
const repoDir = run3.repo ? reposOf(route2).find((t) => t.name === run3.repo)?.dir : void 0;
|
|
12782
|
-
const
|
|
12878
|
+
const num2 = run3.ticket?.replace(/^\D+/, "") ?? "";
|
|
12783
12879
|
return {
|
|
12784
12880
|
role: run3.role,
|
|
12785
12881
|
seat: ownRoute ? seatIds[run3.role] ?? null : null,
|
|
12786
12882
|
ticket: run3.ticket,
|
|
12787
12883
|
ticketRecordId: run3.ticketRecordId,
|
|
12788
12884
|
repo: run3.repo ?? null,
|
|
12789
|
-
worktree: repoDir &&
|
|
12885
|
+
worktree: repoDir && num2 ? worktreeOf(repoDir, num2) : null,
|
|
12790
12886
|
stream: run3.streamPath,
|
|
12791
12887
|
pid: run3.pid,
|
|
12792
12888
|
startedAt: run3.spawnedAt,
|
|
@@ -12842,8 +12938,8 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
|
|
|
12842
12938
|
const tracker = new Tracker(route2, cfg.ship, { apiKey: identity.apiKey });
|
|
12843
12939
|
const t = await tracker.ticket(run3.ticketRecordId);
|
|
12844
12940
|
const target = repoTargetFor(route2, t.repo_id);
|
|
12845
|
-
const
|
|
12846
|
-
const wt = target &&
|
|
12941
|
+
const num2 = String(t[tracker.contract.columns.key] ?? "").replace(/^\D+/, "");
|
|
12942
|
+
const wt = target && num2 ? worktreeForNumber(target.dir, num2) : null;
|
|
12847
12943
|
let dirty = null;
|
|
12848
12944
|
try {
|
|
12849
12945
|
if (wt) dirty = status(wt.path).length > 0;
|
|
@@ -13893,6 +13989,11 @@ function managedRuntimeWarnings(kind, ship) {
|
|
|
13893
13989
|
}
|
|
13894
13990
|
return out;
|
|
13895
13991
|
}
|
|
13992
|
+
function runtimeDirWarning(platform2, uid, exists, user = "the crew user") {
|
|
13993
|
+
if (platform2 !== "linux" || uid === null || uid === 0) return null;
|
|
13994
|
+
if (exists(`/run/user/${uid}`)) return null;
|
|
13995
|
+
return `/run/user/${uid} is missing, so \`systemctl --user\` cannot reach the user manager: lingering is off for ${user}; run \`sudo loginctl enable-linger ${user}\``;
|
|
13996
|
+
}
|
|
13896
13997
|
var MANAGED_GIT_EMAIL = "crew@tablation.dev";
|
|
13897
13998
|
function readGlobalGitIdentity() {
|
|
13898
13999
|
const get2 = (key) => {
|
|
@@ -14139,7 +14240,7 @@ function doctorSelftestLines(stored, toolchains) {
|
|
|
14139
14240
|
// src/cli.ts
|
|
14140
14241
|
import { resolve as resolve7, dirname as dirname35, join as join56, basename as basename16 } from "path";
|
|
14141
14242
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14142
|
-
import { hostname as hostname5, homedir as homedir11 } from "os";
|
|
14243
|
+
import { hostname as hostname5, homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
14143
14244
|
import { spawnSync as spawnSync12 } from "child_process";
|
|
14144
14245
|
import { parse as parseYaml2 } from "yaml";
|
|
14145
14246
|
|
|
@@ -15656,7 +15757,7 @@ function realProvisionDeps(crewHome) {
|
|
|
15656
15757
|
if (mechanism === "systemd") {
|
|
15657
15758
|
const system = ctx.root || existsSync33(systemUnitPathFor(label));
|
|
15658
15759
|
const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
|
|
15659
|
-
running = spawnSync9(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
|
|
15760
|
+
running = spawnSync9(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`], { env: withUserBusEnv() }).status === 0;
|
|
15660
15761
|
if (!system) {
|
|
15661
15762
|
const out = spawnSync9("loginctl", ["show-user", ctx.user, "-p", "Linger"], { encoding: "utf8" });
|
|
15662
15763
|
lingering = /Linger=yes/.test(out.stdout ?? "");
|
|
@@ -18114,10 +18215,10 @@ function dedupeChangelogEntries(entries, ticket) {
|
|
|
18114
18215
|
let key = line;
|
|
18115
18216
|
if (ref) {
|
|
18116
18217
|
const prefix = ref[1];
|
|
18117
|
-
const
|
|
18118
|
-
const canonical = own && own[2] ===
|
|
18119
|
-
line = `${canonical}-${
|
|
18120
|
-
key = `${canonical}-${
|
|
18218
|
+
const num2 = ref[2];
|
|
18219
|
+
const canonical = own && own[2] === num2 ? own[1] : prefix;
|
|
18220
|
+
line = `${canonical}-${num2}${line.slice(ref[0].length)}`;
|
|
18221
|
+
key = `${canonical}-${num2}`;
|
|
18121
18222
|
}
|
|
18122
18223
|
if (seen.has(key)) continue;
|
|
18123
18224
|
seen.add(key);
|
|
@@ -21325,10 +21426,24 @@ async function showLogEntry(route2, entryId, opts = {}) {
|
|
|
21325
21426
|
} catch (e) {
|
|
21326
21427
|
throw new LogbookError(`no Agent Log entry ${entryId} in this workspace (${e.message})`);
|
|
21327
21428
|
}
|
|
21328
|
-
|
|
21429
|
+
let inference;
|
|
21430
|
+
if (entry.run_id && entry.client !== "inference") {
|
|
21431
|
+
try {
|
|
21432
|
+
const rows = await listInferenceRows(
|
|
21433
|
+
{ baseUrl: route2.baseUrl, apiKey: resolveApiKey(route2), userAgent: opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" },
|
|
21434
|
+
agentLogModel.id,
|
|
21435
|
+
entry.run_id
|
|
21436
|
+
);
|
|
21437
|
+
if (rows.length > 0) inference = sumInferenceRows(rows);
|
|
21438
|
+
} catch {
|
|
21439
|
+
}
|
|
21440
|
+
}
|
|
21441
|
+
const withInference = inference ? { inference } : {};
|
|
21442
|
+
if (!opts.withPrompt) return { entry, ...withInference, reconstruction: { status: "not_recorded", reason: "not requested" } };
|
|
21329
21443
|
if (!entry.agent_id || !entry.prompt_version) {
|
|
21330
21444
|
return {
|
|
21331
21445
|
entry,
|
|
21446
|
+
...withInference,
|
|
21332
21447
|
reconstruction: {
|
|
21333
21448
|
status: "not_recorded",
|
|
21334
21449
|
reason: !entry.agent_id ? "this entry has no linked Agents row (predates crew agents sync being run for its role)" : "this entry predates ISSUE-377/529 \u2014 no prompt_version was recorded"
|
|
@@ -21340,12 +21455,13 @@ async function showLogEntry(route2, entryId, opts = {}) {
|
|
|
21340
21455
|
try {
|
|
21341
21456
|
historyEntries = await fetchHistoryEntries(route2, agentsModel.id, entry.agent_id, opts.userAgent, 200);
|
|
21342
21457
|
} catch (e) {
|
|
21343
|
-
return { entry, reconstruction: { status: "approximate", reason: `history lookup failed: ${e.message}` } };
|
|
21458
|
+
return { entry, ...withInference, reconstruction: { status: "approximate", reason: `history lookup failed: ${e.message}` } };
|
|
21344
21459
|
}
|
|
21345
21460
|
const match = historyEntries.find((h) => h.history_id === entry.prompt_version);
|
|
21346
21461
|
if (!match) {
|
|
21347
21462
|
return {
|
|
21348
21463
|
entry,
|
|
21464
|
+
...withInference,
|
|
21349
21465
|
reconstruction: {
|
|
21350
21466
|
status: "approximate",
|
|
21351
21467
|
reason: `history entry ${entry.prompt_version} no longer exists on the Agents row (purged, or the row itself was recreated)`
|
|
@@ -21357,6 +21473,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
|
|
|
21357
21473
|
const exact = !!entry.prompt_sha && actualSha === entry.prompt_sha;
|
|
21358
21474
|
return {
|
|
21359
21475
|
entry,
|
|
21476
|
+
...withInference,
|
|
21360
21477
|
reconstruction: {
|
|
21361
21478
|
status: exact ? "exact" : "approximate",
|
|
21362
21479
|
historyId: match.history_id,
|
|
@@ -21980,8 +22097,8 @@ var BRANCH_STEM = /^(?:.*\/)?([^/]+?-\d+)(?:-.*)?$/;
|
|
|
21980
22097
|
function ticketForWorktree(w, byNumber, contract) {
|
|
21981
22098
|
if (w.branch) {
|
|
21982
22099
|
const stem = BRANCH_STEM.exec(w.branch)?.[1];
|
|
21983
|
-
const
|
|
21984
|
-
const t =
|
|
22100
|
+
const num2 = stem && /-(\d+)$/.exec(stem)?.[1];
|
|
22101
|
+
const t = num2 ? byNumber.get(num2) : void 0;
|
|
21985
22102
|
if (t && ticketStems(t).has(stem)) return t;
|
|
21986
22103
|
}
|
|
21987
22104
|
const dir = basename13(w.path);
|
|
@@ -22161,11 +22278,11 @@ function deployedBranchStems(tickets, contract) {
|
|
|
22161
22278
|
}
|
|
22162
22279
|
function ticketStems(t) {
|
|
22163
22280
|
const stems = /* @__PURE__ */ new Set();
|
|
22164
|
-
const
|
|
22165
|
-
const unpadded = String(Number.parseInt(
|
|
22281
|
+
const num2 = t.issue_id.replace(/^\D+/, "");
|
|
22282
|
+
const unpadded = String(Number.parseInt(num2, 10));
|
|
22166
22283
|
const prefixes = ["issue", t.project_issue_prefix?.toLowerCase()].filter((p) => !!p);
|
|
22167
22284
|
for (const p of prefixes) {
|
|
22168
|
-
stems.add(`${p}-${
|
|
22285
|
+
stems.add(`${p}-${num2}`);
|
|
22169
22286
|
stems.add(`${p}-${unpadded}`);
|
|
22170
22287
|
}
|
|
22171
22288
|
return stems;
|
|
@@ -24143,8 +24260,8 @@ async function reconcileForRoute(ctx, o) {
|
|
|
24143
24260
|
if (!memberId) return;
|
|
24144
24261
|
const t = await tracker.ticket(sc.ticketRecordId);
|
|
24145
24262
|
const target = repoTargetFor(route2, t.repo_id);
|
|
24146
|
-
const
|
|
24147
|
-
const wt = target &&
|
|
24263
|
+
const num2 = String(t[tracker.contract.columns.key] ?? "").replace(/^\D+/, "");
|
|
24264
|
+
const wt = target && num2 ? worktreeForNumber(target.dir, num2) : null;
|
|
24148
24265
|
let dirty = null;
|
|
24149
24266
|
try {
|
|
24150
24267
|
if (wt) dirty = status(wt.path).length > 0;
|
|
@@ -24178,8 +24295,8 @@ async function postRunKilled(route2, tracker, ticketId, memberId, limitMinutes,
|
|
|
24178
24295
|
try {
|
|
24179
24296
|
const t = await tracker.ticket(ticketId);
|
|
24180
24297
|
const target = repoTargetFor(route2, t.repo_id);
|
|
24181
|
-
const
|
|
24182
|
-
const wt = target &&
|
|
24298
|
+
const num2 = String(t[tracker.contract.columns.key] ?? "").replace(/^\D+/, "");
|
|
24299
|
+
const wt = target && num2 ? worktreeForNumber(target.dir, num2) : null;
|
|
24183
24300
|
let dirty = null;
|
|
24184
24301
|
try {
|
|
24185
24302
|
if (wt) dirty = status(wt.path).length > 0;
|
|
@@ -25527,10 +25644,10 @@ async function handlePeriscopeMcp(d, req, res, segment) {
|
|
|
25527
25644
|
function findPeriscope(sidecars, segment) {
|
|
25528
25645
|
const m = /^(?:([A-Za-z]+)-)?(\d+)$/.exec(segment);
|
|
25529
25646
|
if (!m) return void 0;
|
|
25530
|
-
const
|
|
25647
|
+
const num2 = String(Number(m[2]));
|
|
25531
25648
|
return sidecars.find((sc) => {
|
|
25532
25649
|
const t = /^(?:([A-Za-z]+)-)?0*(\d+)$/.exec(sc.ticket);
|
|
25533
|
-
if (!t || t[2] !==
|
|
25650
|
+
if (!t || t[2] !== num2) return false;
|
|
25534
25651
|
return !m[1] || !t[1] || m[1].toLowerCase() === t[1].toLowerCase();
|
|
25535
25652
|
});
|
|
25536
25653
|
}
|
|
@@ -26514,7 +26631,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
26514
26631
|
`
|
|
26515
26632
|
);
|
|
26516
26633
|
if (!dryRun) {
|
|
26517
|
-
spawnSync12("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
|
|
26634
|
+
spawnSync12("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME, env: withUserBusEnv() });
|
|
26518
26635
|
try {
|
|
26519
26636
|
unlinkSync5(staleTimer);
|
|
26520
26637
|
} catch {
|
|
@@ -27810,7 +27927,7 @@ ${cu.tail.trimEnd()}
|
|
|
27810
27927
|
}
|
|
27811
27928
|
let failed = false;
|
|
27812
27929
|
for (const cmd2 of commands) {
|
|
27813
|
-
const res2 = spawnSync12(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
27930
|
+
const res2 = spawnSync12(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8", env: withUserBusEnv() });
|
|
27814
27931
|
if (res2.stdout) process.stdout.write(res2.stdout);
|
|
27815
27932
|
if (res2.stderr) process.stderr.write(res2.stderr);
|
|
27816
27933
|
if (cmd2 === control.startCommand && (res2.error || res2.status !== 0)) {
|
|
@@ -27828,7 +27945,7 @@ ${cu.tail.trimEnd()}
|
|
|
27828
27945
|
`);
|
|
27829
27946
|
break;
|
|
27830
27947
|
}
|
|
27831
|
-
const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
27948
|
+
const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8", env: withUserBusEnv() });
|
|
27832
27949
|
if (res.stdout) process.stdout.write(res.stdout);
|
|
27833
27950
|
if (res.stderr) process.stderr.write(res.stderr);
|
|
27834
27951
|
if (daemonSub !== "status" && (res.error || res.status !== 0)) {
|
|
@@ -28618,7 +28735,7 @@ ${cu.tail.trimEnd()}
|
|
|
28618
28735
|
);
|
|
28619
28736
|
}
|
|
28620
28737
|
} else {
|
|
28621
|
-
const { entry, reconstruction } = await showLogEntry(target, entryId, {
|
|
28738
|
+
const { entry, inference, reconstruction } = await showLogEntry(target, entryId, {
|
|
28622
28739
|
userAgent: cfg.ship.userAgent,
|
|
28623
28740
|
withPrompt: flag("prompt")
|
|
28624
28741
|
});
|
|
@@ -28632,6 +28749,9 @@ model: ${entry.model ?? "?"}
|
|
|
28632
28749
|
harness: ${entry.harness ?? "?"}
|
|
28633
28750
|
provider: ${entry.provider ?? "?"}
|
|
28634
28751
|
` + (entry.num_turns != null ? `turns: ${entry.num_turns}
|
|
28752
|
+
` : "") + (inference ? `calls: ${inference.calls}
|
|
28753
|
+
tokens: in ${inference.tokensIn}, out ${inference.tokensOut}, cache read ${inference.cacheReadTokens}, cache write ${inference.cacheWriteTokens}
|
|
28754
|
+
cost: $${inference.costUsd.toFixed(4)}
|
|
28635
28755
|
` : "") + (entry.prefix_tokens != null ? `prefix: ${entry.prefix_tokens} tokens
|
|
28636
28756
|
` : "") + (formatBeforeEdit(entry) ? `before edit: ${formatBeforeEdit(entry)}
|
|
28637
28757
|
` : "")
|
|
@@ -29674,7 +29794,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
29674
29794
|
process.stderr.write("drop: need a ticket number (326, ISSUE-326, or TABL-326)\n");
|
|
29675
29795
|
process.exit(2);
|
|
29676
29796
|
}
|
|
29677
|
-
const
|
|
29797
|
+
const num2 = Number(n);
|
|
29678
29798
|
const repos = await resolvedRepos(route2);
|
|
29679
29799
|
const candidates2 = repos.map((r) => ({ repo: r, found: worktreeForNumber(r.dir, n) })).filter((c) => c.found !== null);
|
|
29680
29800
|
if (candidates2.length === 0) {
|
|
@@ -29695,9 +29815,9 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
29695
29815
|
const wt = found.path;
|
|
29696
29816
|
try {
|
|
29697
29817
|
const trD = new Tracker(route2, cfg.ship);
|
|
29698
|
-
const tD = (await trD.ticketsByNumbers([n])).find((t) => t.issue_id.replace(/^\D+/, "") === String(
|
|
29818
|
+
const tD = (await trD.ticketsByNumbers([n])).find((t) => t.issue_id.replace(/^\D+/, "") === String(num2));
|
|
29699
29819
|
if (tD && tD[trD.contract.columns.status] === trD.contract.statuses.draft) {
|
|
29700
|
-
process.stderr.write(`drop: ${tD.issue_tag ?? tD.issue_id} is a draft ticket \u2014 its worktree belongs to a periscope; end it with \`crew periscope down ${
|
|
29820
|
+
process.stderr.write(`drop: ${tD.issue_tag ?? tD.issue_id} is a draft ticket \u2014 its worktree belongs to a periscope; end it with \`crew periscope down ${num2} --keep\` or \`--discard\`
|
|
29701
29821
|
`);
|
|
29702
29822
|
process.exit(2);
|
|
29703
29823
|
}
|
|
@@ -29707,7 +29827,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
29707
29827
|
name: target.config.branch.name,
|
|
29708
29828
|
push: target.config.branch.push
|
|
29709
29829
|
}, (t) => renderBranchName(t, { key: `ISSUE-${n}`, lowercase: target.config.branch.lowercase }));
|
|
29710
|
-
const pids = pidsInWorktree(wt,
|
|
29830
|
+
const pids = pidsInWorktree(wt, num2);
|
|
29711
29831
|
if (pids.length) {
|
|
29712
29832
|
emit.emit(`${dryRun ? "would kill" : "killing"} processes still inside ${wt}: ${pids.join(" ")}`);
|
|
29713
29833
|
if (!dryRun) await killGently(pids);
|
|
@@ -30541,7 +30661,7 @@ ${where.join("\n")}`);
|
|
|
30541
30661
|
}
|
|
30542
30662
|
const host = hostPlatform();
|
|
30543
30663
|
const control = host === "windows" ? null : planDaemonControl(CREW_HOME, host, detectSystemd());
|
|
30544
|
-
const unitStatus = control?.statusCommand ? spawnSync12(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
|
|
30664
|
+
const unitStatus = control?.statusCommand ? spawnSync12(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8", env: withUserBusEnv() }) : null;
|
|
30545
30665
|
const unitRunning = unitStatus !== null && !unitStatus.error && unitStatus.status === 0;
|
|
30546
30666
|
if (!unitRunning && control?.mechanism === "systemd" && control.startCommand) {
|
|
30547
30667
|
const res = spawnSync12(control.startCommand[0], control.startCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
@@ -30566,7 +30686,7 @@ ${where.join("\n")}`);
|
|
|
30566
30686
|
);
|
|
30567
30687
|
if (flag("now") && control?.stopCommand && control.startCommand) {
|
|
30568
30688
|
for (const cmd of [control.stopCommand, control.startCommand]) {
|
|
30569
|
-
const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
30689
|
+
const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8", env: withUserBusEnv() });
|
|
30570
30690
|
if (cmd === control.startCommand && (res.error || res.status !== 0)) {
|
|
30571
30691
|
process.stderr.write(`crew reload: ${cmd.join(" ")} exited ${res.status ?? "error"}
|
|
30572
30692
|
`);
|
|
@@ -31144,6 +31264,8 @@ watermark: ${state.watermark()}
|
|
|
31144
31264
|
}
|
|
31145
31265
|
}
|
|
31146
31266
|
for (const message of roleCapWarnings(cfg.ship)) problems.push({ severity: "note", kind: "role_cap_above_ship_cap", message });
|
|
31267
|
+
const runtimeDir = runtimeDirWarning(process.platform, process.getuid?.() ?? null, existsSync51, userInfo2().username);
|
|
31268
|
+
if (runtimeDir) problems.push({ severity: "attention", kind: "runtime_dir_missing", message: runtimeDir });
|
|
31147
31269
|
if (!flag("offline")) {
|
|
31148
31270
|
const shipKind = await doctorShipKind(route2, cfg.ship);
|
|
31149
31271
|
for (const message of managedRuntimeWarnings(shipKind, cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
|
package/package.json
CHANGED